From 84c1f7f53580c903fa532aa900a74f856f3e9357 Mon Sep 17 00:00:00 2001 From: FranzHaidnor Date: Wed, 25 Oct 2023 10:17:12 +0800 Subject: [PATCH] refactor bcel lib --- pom.xml | 21 +- src/main/java/haidnor/jvm/Main.java | 91 - src/main/java/haidnor/jvm/bcel/Const.java | 3234 +++++++++++++++++ .../java/haidnor/jvm/bcel/ExceptionConst.java | 128 + .../java/haidnor/jvm/bcel/Repository.java | 215 ++ .../jvm/bcel/classfile/AccessFlags.java | 211 ++ .../jvm/bcel/classfile/AnnotationDefault.java | 92 + .../classfile/AnnotationElementValue.java | 56 + .../jvm/bcel/classfile/AnnotationEntry.java | 162 + .../jvm/bcel/classfile/Annotations.java | 158 + .../jvm/bcel/classfile/ArrayElementValue.java | 81 + .../haidnor/jvm/bcel/classfile/Attribute.java | 367 ++ .../jvm/bcel/classfile/BootstrapMethod.java | 166 + .../jvm/bcel/classfile/BootstrapMethods.java | 159 + .../jvm/bcel/classfile/ClassElementValue.java | 54 + .../bcel/classfile/ClassFormatException.java | 68 + .../jvm/bcel/classfile/ClassParser.java | 288 ++ .../java/haidnor/jvm/bcel/classfile/Code.java | 347 ++ .../jvm/bcel/classfile/CodeException.java | 232 ++ .../haidnor/jvm/bcel/classfile/Constant.java | 199 + .../jvm/bcel/classfile/ConstantCP.java | 140 + .../jvm/bcel/classfile/ConstantClass.java | 120 + .../jvm/bcel/classfile/ConstantDouble.java | 113 + .../jvm/bcel/classfile/ConstantDynamic.java | 86 + .../jvm/bcel/classfile/ConstantFieldref.java | 66 + .../jvm/bcel/classfile/ConstantFloat.java | 114 + .../jvm/bcel/classfile/ConstantInteger.java | 113 + .../classfile/ConstantInterfaceMethodref.java | 66 + .../bcel/classfile/ConstantInvokeDynamic.java | 85 + .../jvm/bcel/classfile/ConstantLong.java | 113 + .../bcel/classfile/ConstantMethodHandle.java | 108 + .../bcel/classfile/ConstantMethodType.java | 97 + .../jvm/bcel/classfile/ConstantMethodref.java | 66 + .../jvm/bcel/classfile/ConstantModule.java | 125 + .../bcel/classfile/ConstantNameAndType.java | 138 + .../jvm/bcel/classfile/ConstantObject.java | 30 + .../jvm/bcel/classfile/ConstantPackage.java | 125 + .../jvm/bcel/classfile/ConstantPool.java | 433 +++ .../jvm/bcel/classfile/ConstantString.java | 120 + .../jvm/bcel/classfile/ConstantUtf8.java | 259 ++ .../jvm/bcel/classfile/ConstantValue.java | 156 + .../jvm/bcel/classfile/Deprecated.java | 134 + .../jvm/bcel/classfile/DescendingVisitor.java | 547 +++ .../jvm/bcel/classfile/ElementValue.java | 174 + .../jvm/bcel/classfile/ElementValuePair.java | 65 + .../jvm/bcel/classfile/EmptyVisitor.java | 320 ++ .../jvm/bcel/classfile/EnclosingMethod.java | 102 + .../jvm/bcel/classfile/EnumElementValue.java | 67 + .../jvm/bcel/classfile/ExceptionTable.java | 176 + .../jvm/bcel/classfile/FieldOrMethod.java | 260 ++ .../jvm/bcel/classfile/InnerClass.java | 193 + .../jvm/bcel/classfile/InnerClasses.java | 156 + .../haidnor/jvm/bcel/classfile/JavaClass.java | 862 +++++ .../haidnor/jvm/bcel/classfile/JavaField.java | 193 + .../jvm/bcel/classfile/JavaMethod.java | 257 ++ .../jvm/bcel/classfile/LineNumber.java | 138 + .../jvm/bcel/classfile/LineNumberTable.java | 213 ++ .../jvm/bcel/classfile/LocalVariable.java | 282 ++ .../bcel/classfile/LocalVariableTable.java | 191 + .../classfile/LocalVariableTypeTable.java | 157 + .../jvm/bcel/classfile/MethodParameter.java | 121 + .../jvm/bcel/classfile/MethodParameters.java | 97 + .../haidnor/jvm/bcel/classfile/Module.java | 250 ++ .../jvm/bcel/classfile/ModuleExports.java | 119 + .../jvm/bcel/classfile/ModuleMainClass.java | 130 + .../jvm/bcel/classfile/ModuleOpens.java | 119 + .../jvm/bcel/classfile/ModulePackages.java | 163 + .../jvm/bcel/classfile/ModuleProvides.java | 115 + .../jvm/bcel/classfile/ModuleRequires.java | 108 + .../haidnor/jvm/bcel/classfile/NestHost.java | 130 + .../jvm/bcel/classfile/NestMembers.java | 164 + .../java/haidnor/jvm/bcel/classfile/Node.java | 25 + .../haidnor/jvm/bcel/classfile/PMGClass.java | 152 + .../classfile/ParameterAnnotationEntry.java | 88 + .../bcel/classfile/ParameterAnnotations.java | 120 + .../RuntimeInvisibleAnnotations.java | 56 + .../RuntimeInvisibleParameterAnnotations.java | 42 + .../classfile/RuntimeVisibleAnnotations.java | 56 + .../RuntimeVisibleParameterAnnotations.java | 42 + .../haidnor/jvm/bcel/classfile/Signature.java | 253 ++ .../bcel/classfile/SimpleElementValue.java | 178 + .../jvm/bcel/classfile/SourceFile.java | 133 + .../haidnor/jvm/bcel/classfile/StackMap.java | 161 + .../jvm/bcel/classfile/StackMapEntry.java | 415 +++ .../jvm/bcel/classfile/StackMapType.java | 157 + .../haidnor/jvm/bcel/classfile/Synthetic.java | 140 + .../haidnor/jvm/bcel/classfile/Unknown.java | 159 + .../classfile/UnknownAttributeReader.java | 46 + .../haidnor/jvm/bcel/classfile/Utility.java | 1547 ++++++++ .../haidnor/jvm/bcel/classfile/Visitor.java | 231 ++ .../java/haidnor/jvm/bcel/generic/AALOAD.java | 51 + .../haidnor/jvm/bcel/generic/AASTORE.java | 51 + .../haidnor/jvm/bcel/generic/ACONST_NULL.java | 58 + .../java/haidnor/jvm/bcel/generic/ALOAD.java | 57 + .../haidnor/jvm/bcel/generic/ANEWARRAY.java | 71 + .../haidnor/jvm/bcel/generic/ARETURN.java | 51 + .../haidnor/jvm/bcel/generic/ARRAYLENGTH.java | 58 + .../java/haidnor/jvm/bcel/generic/ASTORE.java | 57 + .../java/haidnor/jvm/bcel/generic/ATHROW.java | 58 + .../bcel/generic/AllocationInstruction.java | 23 + .../generic/AnnotationElementValueGen.java | 72 + .../jvm/bcel/generic/AnnotationEntryGen.java | 326 ++ .../bcel/generic/ArithmeticInstruction.java | 90 + .../bcel/generic/ArrayElementValueGen.java | 111 + .../jvm/bcel/generic/ArrayInstruction.java | 80 + .../haidnor/jvm/bcel/generic/ArrayType.java | 135 + .../java/haidnor/jvm/bcel/generic/BALOAD.java | 51 + .../haidnor/jvm/bcel/generic/BASTORE.java | 51 + .../java/haidnor/jvm/bcel/generic/BIPUSH.java | 103 + .../haidnor/jvm/bcel/generic/BREAKPOINT.java | 40 + .../haidnor/jvm/bcel/generic/BasicType.java | 80 + .../jvm/bcel/generic/BranchHandle.java | 100 + .../jvm/bcel/generic/BranchInstruction.java | 256 ++ .../java/haidnor/jvm/bcel/generic/CALOAD.java | 51 + .../haidnor/jvm/bcel/generic/CASTORE.java | 51 + .../haidnor/jvm/bcel/generic/CHECKCAST.java | 79 + .../jvm/bcel/generic/CPInstruction.java | 141 + .../bcel/generic/ClassElementValueGen.java | 87 + .../haidnor/jvm/bcel/generic/ClassGen.java | 528 +++ .../jvm/bcel/generic/ClassGenException.java | 36 + .../jvm/bcel/generic/ClassObserver.java | 26 + .../jvm/bcel/generic/CodeExceptionGen.java | 176 + .../jvm/bcel/generic/CompoundInstruction.java | 33 + .../jvm/bcel/generic/ConstantPoolGen.java | 756 ++++ .../bcel/generic/ConstantPushInstruction.java | 29 + .../bcel/generic/ConversionInstruction.java | 72 + .../java/haidnor/jvm/bcel/generic/D2F.java | 51 + .../java/haidnor/jvm/bcel/generic/D2I.java | 51 + .../java/haidnor/jvm/bcel/generic/D2L.java | 51 + .../java/haidnor/jvm/bcel/generic/DADD.java | 53 + .../java/haidnor/jvm/bcel/generic/DALOAD.java | 51 + .../haidnor/jvm/bcel/generic/DASTORE.java | 51 + .../java/haidnor/jvm/bcel/generic/DCMPG.java | 55 + .../java/haidnor/jvm/bcel/generic/DCMPL.java | 55 + .../java/haidnor/jvm/bcel/generic/DCONST.java | 78 + .../java/haidnor/jvm/bcel/generic/DDIV.java | 53 + .../java/haidnor/jvm/bcel/generic/DLOAD.java | 57 + .../java/haidnor/jvm/bcel/generic/DMUL.java | 53 + .../java/haidnor/jvm/bcel/generic/DNEG.java | 48 + .../java/haidnor/jvm/bcel/generic/DREM.java | 53 + .../haidnor/jvm/bcel/generic/DRETURN.java | 51 + .../java/haidnor/jvm/bcel/generic/DSTORE.java | 57 + .../java/haidnor/jvm/bcel/generic/DSUB.java | 53 + .../java/haidnor/jvm/bcel/generic/DUP.java | 47 + .../java/haidnor/jvm/bcel/generic/DUP2.java | 47 + .../haidnor/jvm/bcel/generic/DUP2_X1.java | 45 + .../haidnor/jvm/bcel/generic/DUP2_X2.java | 45 + .../java/haidnor/jvm/bcel/generic/DUP_X1.java | 45 + .../java/haidnor/jvm/bcel/generic/DUP_X2.java | 45 + .../jvm/bcel/generic/ElementValueGen.java | 159 + .../jvm/bcel/generic/ElementValuePairGen.java | 96 + .../jvm/bcel/generic/EmptyVisitor.java | 750 ++++ .../jvm/bcel/generic/EnumElementValueGen.java | 120 + .../jvm/bcel/generic/ExceptionThrower.java | 33 + .../java/haidnor/jvm/bcel/generic/F2D.java | 51 + .../java/haidnor/jvm/bcel/generic/F2I.java | 51 + .../java/haidnor/jvm/bcel/generic/F2L.java | 51 + .../java/haidnor/jvm/bcel/generic/FADD.java | 51 + .../java/haidnor/jvm/bcel/generic/FALOAD.java | 51 + .../haidnor/jvm/bcel/generic/FASTORE.java | 51 + .../java/haidnor/jvm/bcel/generic/FCMPG.java | 55 + .../java/haidnor/jvm/bcel/generic/FCMPL.java | 55 + .../java/haidnor/jvm/bcel/generic/FCONST.java | 80 + .../java/haidnor/jvm/bcel/generic/FDIV.java | 51 + .../java/haidnor/jvm/bcel/generic/FLOAD.java | 57 + .../java/haidnor/jvm/bcel/generic/FMUL.java | 51 + .../java/haidnor/jvm/bcel/generic/FNEG.java | 48 + .../java/haidnor/jvm/bcel/generic/FREM.java | 51 + .../haidnor/jvm/bcel/generic/FRETURN.java | 51 + .../java/haidnor/jvm/bcel/generic/FSTORE.java | 57 + .../java/haidnor/jvm/bcel/generic/FSUB.java | 51 + .../haidnor/jvm/bcel/generic/FieldGen.java | 336 ++ .../jvm/bcel/generic/FieldGenOrMethodGen.java | 172 + .../jvm/bcel/generic/FieldInstruction.java | 76 + .../jvm/bcel/generic/FieldObserver.java | 26 + .../jvm/bcel/generic/FieldOrMethod.java | 125 + .../haidnor/jvm/bcel/generic/GETFIELD.java | 76 + .../haidnor/jvm/bcel/generic/GETSTATIC.java | 75 + .../java/haidnor/jvm/bcel/generic/GOTO.java | 92 + .../java/haidnor/jvm/bcel/generic/GOTO_W.java | 75 + .../jvm/bcel/generic/GotoInstruction.java | 33 + .../java/haidnor/jvm/bcel/generic/I2B.java | 51 + .../java/haidnor/jvm/bcel/generic/I2C.java | 51 + .../java/haidnor/jvm/bcel/generic/I2D.java | 51 + .../java/haidnor/jvm/bcel/generic/I2F.java | 51 + .../java/haidnor/jvm/bcel/generic/I2L.java | 51 + .../java/haidnor/jvm/bcel/generic/I2S.java | 48 + .../java/haidnor/jvm/bcel/generic/IADD.java | 51 + .../java/haidnor/jvm/bcel/generic/IALOAD.java | 51 + .../java/haidnor/jvm/bcel/generic/IAND.java | 48 + .../haidnor/jvm/bcel/generic/IASTORE.java | 51 + .../java/haidnor/jvm/bcel/generic/ICONST.java | 75 + .../java/haidnor/jvm/bcel/generic/IDIV.java | 61 + .../java/haidnor/jvm/bcel/generic/IFEQ.java | 61 + .../java/haidnor/jvm/bcel/generic/IFGE.java | 61 + .../java/haidnor/jvm/bcel/generic/IFGT.java | 61 + .../java/haidnor/jvm/bcel/generic/IFLE.java | 61 + .../java/haidnor/jvm/bcel/generic/IFLT.java | 61 + .../java/haidnor/jvm/bcel/generic/IFNE.java | 61 + .../haidnor/jvm/bcel/generic/IFNONNULL.java | 61 + .../java/haidnor/jvm/bcel/generic/IFNULL.java | 61 + .../haidnor/jvm/bcel/generic/IF_ACMPEQ.java | 61 + .../haidnor/jvm/bcel/generic/IF_ACMPNE.java | 61 + .../haidnor/jvm/bcel/generic/IF_ICMPEQ.java | 61 + .../haidnor/jvm/bcel/generic/IF_ICMPGE.java | 61 + .../haidnor/jvm/bcel/generic/IF_ICMPGT.java | 61 + .../haidnor/jvm/bcel/generic/IF_ICMPLE.java | 61 + .../haidnor/jvm/bcel/generic/IF_ICMPLT.java | 61 + .../haidnor/jvm/bcel/generic/IF_ICMPNE.java | 61 + .../java/haidnor/jvm/bcel/generic/IINC.java | 158 + .../java/haidnor/jvm/bcel/generic/ILOAD.java | 57 + .../haidnor/jvm/bcel/generic/IMPDEP1.java | 40 + .../haidnor/jvm/bcel/generic/IMPDEP2.java | 40 + .../java/haidnor/jvm/bcel/generic/IMUL.java | 51 + .../java/haidnor/jvm/bcel/generic/INEG.java | 48 + .../haidnor/jvm/bcel/generic/INSTANCEOF.java | 71 + .../jvm/bcel/generic/INVOKEDYNAMIC.java | 124 + .../jvm/bcel/generic/INVOKEINTERFACE.java | 124 + .../jvm/bcel/generic/INVOKESPECIAL.java | 83 + .../jvm/bcel/generic/INVOKESTATIC.java | 82 + .../jvm/bcel/generic/INVOKEVIRTUAL.java | 82 + .../java/haidnor/jvm/bcel/generic/IOR.java | 48 + .../java/haidnor/jvm/bcel/generic/IREM.java | 61 + .../haidnor/jvm/bcel/generic/IRETURN.java | 51 + .../java/haidnor/jvm/bcel/generic/ISHL.java | 48 + .../java/haidnor/jvm/bcel/generic/ISHR.java | 48 + .../java/haidnor/jvm/bcel/generic/ISTORE.java | 57 + .../java/haidnor/jvm/bcel/generic/ISUB.java | 51 + .../java/haidnor/jvm/bcel/generic/IUSHR.java | 48 + .../java/haidnor/jvm/bcel/generic/IXOR.java | 48 + .../jvm/bcel/generic/IfInstruction.java | 42 + .../jvm/bcel/generic/IndexedInstruction.java | 27 + .../haidnor/jvm/bcel/generic/Instruction.java | 596 +++ .../bcel/generic/InstructionComparator.java | 55 + .../jvm/bcel/generic/InstructionConst.java | 294 ++ .../jvm/bcel/generic/InstructionFactory.java | 734 ++++ .../jvm/bcel/generic/InstructionHandle.java | 300 ++ .../jvm/bcel/generic/InstructionList.java | 1185 ++++++ .../bcel/generic/InstructionListObserver.java | 26 + .../jvm/bcel/generic/InstructionTargeter.java | 44 + .../jvm/bcel/generic/InvokeInstruction.java | 140 + .../java/haidnor/jvm/bcel/generic/JSR.java | 83 + .../java/haidnor/jvm/bcel/generic/JSR_W.java | 75 + .../jvm/bcel/generic/JsrInstruction.java | 69 + .../java/haidnor/jvm/bcel/generic/L2D.java | 48 + .../java/haidnor/jvm/bcel/generic/L2F.java | 48 + .../java/haidnor/jvm/bcel/generic/L2I.java | 48 + .../java/haidnor/jvm/bcel/generic/LADD.java | 50 + .../java/haidnor/jvm/bcel/generic/LALOAD.java | 51 + .../java/haidnor/jvm/bcel/generic/LAND.java | 50 + .../haidnor/jvm/bcel/generic/LASTORE.java | 51 + .../java/haidnor/jvm/bcel/generic/LCMP.java | 56 + .../java/haidnor/jvm/bcel/generic/LCONST.java | 78 + .../java/haidnor/jvm/bcel/generic/LDC.java | 147 + .../java/haidnor/jvm/bcel/generic/LDC2_W.java | 81 + .../java/haidnor/jvm/bcel/generic/LDC_W.java | 53 + .../java/haidnor/jvm/bcel/generic/LDIV.java | 57 + .../java/haidnor/jvm/bcel/generic/LLOAD.java | 52 + .../java/haidnor/jvm/bcel/generic/LMUL.java | 50 + .../java/haidnor/jvm/bcel/generic/LNEG.java | 48 + .../jvm/bcel/generic/LOOKUPSWITCH.java | 97 + .../java/haidnor/jvm/bcel/generic/LOR.java | 48 + .../java/haidnor/jvm/bcel/generic/LREM.java | 55 + .../haidnor/jvm/bcel/generic/LRETURN.java | 48 + .../java/haidnor/jvm/bcel/generic/LSHL.java | 48 + .../java/haidnor/jvm/bcel/generic/LSHR.java | 48 + .../java/haidnor/jvm/bcel/generic/LSTORE.java | 52 + .../java/haidnor/jvm/bcel/generic/LSUB.java | 50 + .../java/haidnor/jvm/bcel/generic/LUSHR.java | 48 + .../java/haidnor/jvm/bcel/generic/LXOR.java | 48 + .../jvm/bcel/generic/LineNumberGen.java | 103 + .../haidnor/jvm/bcel/generic/LoadClass.java | 43 + .../jvm/bcel/generic/LoadInstruction.java | 55 + .../jvm/bcel/generic/LocalVariableGen.java | 232 ++ .../generic/LocalVariableInstruction.java | 215 ++ .../jvm/bcel/generic/MONITORENTER.java | 52 + .../haidnor/jvm/bcel/generic/MONITOREXIT.java | 52 + .../jvm/bcel/generic/MULTIANEWARRAY.java | 138 + .../haidnor/jvm/bcel/generic/MethodGen.java | 1152 ++++++ .../jvm/bcel/generic/MethodObserver.java | 26 + .../java/haidnor/jvm/bcel/generic/NEW.java | 68 + .../haidnor/jvm/bcel/generic/NEWARRAY.java | 114 + .../java/haidnor/jvm/bcel/generic/NOP.java | 40 + .../generic/NameSignatureInstruction.java | 63 + .../jvm/bcel/generic/NamedAndTyped.java | 31 + .../haidnor/jvm/bcel/generic/ObjectType.java | 154 + .../java/haidnor/jvm/bcel/generic/POP.java | 47 + .../java/haidnor/jvm/bcel/generic/POP2.java | 47 + .../java/haidnor/jvm/bcel/generic/PUSH.java | 195 + .../haidnor/jvm/bcel/generic/PUTFIELD.java | 76 + .../haidnor/jvm/bcel/generic/PUTSTATIC.java | 75 + .../jvm/bcel/generic/PopInstruction.java | 26 + .../jvm/bcel/generic/PushInstruction.java | 32 + .../java/haidnor/jvm/bcel/generic/RET.java | 136 + .../java/haidnor/jvm/bcel/generic/RETURN.java | 48 + .../jvm/bcel/generic/ReferenceType.java | 260 ++ .../jvm/bcel/generic/ReturnInstruction.java | 72 + .../jvm/bcel/generic/ReturnaddressType.java | 78 + .../java/haidnor/jvm/bcel/generic/SALOAD.java | 48 + .../haidnor/jvm/bcel/generic/SASTORE.java | 48 + .../java/haidnor/jvm/bcel/generic/SIPUSH.java | 100 + .../java/haidnor/jvm/bcel/generic/SWAP.java | 47 + .../java/haidnor/jvm/bcel/generic/SWITCH.java | 134 + .../java/haidnor/jvm/bcel/generic/Select.java | 368 ++ .../bcel/generic/SimpleElementValueGen.java | 228 ++ .../jvm/bcel/generic/StackConsumer.java | 28 + .../jvm/bcel/generic/StackInstruction.java | 43 + .../jvm/bcel/generic/StackProducer.java | 28 + .../jvm/bcel/generic/StoreInstruction.java | 55 + .../haidnor/jvm/bcel/generic/TABLESWITCH.java | 105 + .../jvm/bcel/generic/TargetLostException.java | 62 + .../java/haidnor/jvm/bcel/generic/Type.java | 395 ++ .../jvm/bcel/generic/TypedInstruction.java | 25 + .../jvm/bcel/generic/UnconditionalBranch.java | 26 + .../generic/VariableLengthInstruction.java | 29 + .../haidnor/jvm/bcel/generic/Visitor.java | 389 ++ .../util/AbstractClassPathRepository.java | 129 + src/main/java/haidnor/jvm/bcel/util/Args.java | 144 + .../haidnor/jvm/bcel/util/BCELComparator.java | 42 + .../haidnor/jvm/bcel/util/BCELFactory.java | 302 ++ .../java/haidnor/jvm/bcel/util/BCELifier.java | 311 ++ .../haidnor/jvm/bcel/util/ByteSequence.java | 60 + .../java/haidnor/jvm/bcel/util/ClassPath.java | 781 ++++ .../haidnor/jvm/bcel/util/ClassQueue.java | 50 + .../haidnor/jvm/bcel/util/JavaWrapper.java | 115 + .../MemorySensitiveClassPathRepository.java | 76 + .../jvm/bcel/util/ModularRuntimeImage.java | 132 + .../haidnor/jvm/bcel/util/Repository.java | 67 + .../jvm/bcel/util/SyntheticRepository.java | 50 + .../haidnor/jvm/classloader/ClassLoader.java | 86 - .../haidnor/jvm/core/JavaExecutionEngine.java | 173 - .../haidnor/jvm/instruction/Instruction.java | 48 - .../jvm/instruction/InstructionFactory.java | 631 ---- .../jvm/instruction/comparisons/DCMPG.java | 31 - .../jvm/instruction/comparisons/DCMPL.java | 31 - .../jvm/instruction/comparisons/FCMPG.java | 30 - .../jvm/instruction/comparisons/FCMPL.java | 30 - .../jvm/instruction/comparisons/IFEQ.java | 40 - .../jvm/instruction/comparisons/IFGE.java | 34 - .../jvm/instruction/comparisons/IFGT.java | 34 - .../jvm/instruction/comparisons/IFLE.java | 34 - .../jvm/instruction/comparisons/IFLT.java | 34 - .../jvm/instruction/comparisons/IFNE.java | 43 - .../instruction/comparisons/IF_ACMPEQ.java | 36 - .../instruction/comparisons/IF_ACMPNE.java | 36 - .../instruction/comparisons/IF_ICMPEQ.java | 42 - .../instruction/comparisons/IF_ICMPGE.java | 42 - .../instruction/comparisons/IF_ICMPGT.java | 42 - .../instruction/comparisons/IF_ICMPLE.java | 37 - .../instruction/comparisons/IF_ICMPLT.java | 42 - .../instruction/comparisons/IF_ICMPNE.java | 42 - .../jvm/instruction/comparisons/LCMP.java | 28 - .../instruction/constants/ACONST_NULL.java | 20 - .../jvm/instruction/constants/BIPUSH.java | 29 - .../jvm/instruction/constants/DCONST_0.java | 26 - .../jvm/instruction/constants/DCONST_1.java | 26 - .../jvm/instruction/constants/FCONST_1.java | 26 - .../jvm/instruction/constants/FCONST_2.java | 26 - .../jvm/instruction/constants/ICONST_0.java | 23 - .../jvm/instruction/constants/ICONST_1.java | 23 - .../jvm/instruction/constants/ICONST_2.java | 23 - .../jvm/instruction/constants/ICONST_3.java | 23 - .../jvm/instruction/constants/ICONST_4.java | 23 - .../jvm/instruction/constants/ICONST_5.java | 23 - .../jvm/instruction/constants/ICONST_M1.java | 23 - .../jvm/instruction/constants/LCONST_0.java | 26 - .../jvm/instruction/constants/LCONST_1.java | 26 - .../jvm/instruction/constants/LDC.java | 58 - .../jvm/instruction/constants/LDC2W.java | 48 - .../jvm/instruction/constants/LDC_W.java | 58 - .../jvm/instruction/constants/NOP.java | 23 - .../jvm/instruction/constants/SIPUSH.java | 29 - .../jvm/instruction/control/ARETURN.java | 25 - .../jvm/instruction/control/DRETURN.java | 25 - .../jvm/instruction/control/FRETURN.java | 25 - .../jvm/instruction/control/IRETURN.java | 25 - .../jvm/instruction/control/LOOKUPSWITCH.java | 19 - .../jvm/instruction/control/LRETURN.java | 25 - .../haidnor/jvm/instruction/control/RET.java | 28 - .../jvm/instruction/control/RETURN.java | 18 - .../control/ReturnableInstruction.java | 15 - .../jvm/instruction/control/TABLESWITCH.java | 19 - .../jvm/instruction/conversions/D2F.java | 20 - .../jvm/instruction/conversions/D2I.java | 19 - .../jvm/instruction/conversions/D2L.java | 19 - .../jvm/instruction/conversions/F2D.java | 19 - .../jvm/instruction/conversions/F2I.java | 19 - .../jvm/instruction/conversions/F2L.java | 19 - .../jvm/instruction/conversions/I2B.java | 24 - .../jvm/instruction/conversions/I2C.java | 21 - .../jvm/instruction/conversions/I2D.java | 19 - .../jvm/instruction/conversions/I2F.java | 19 - .../jvm/instruction/conversions/I2L.java | 19 - .../jvm/instruction/conversions/I2S.java | 20 - .../jvm/instruction/conversions/L2D.java | 19 - .../jvm/instruction/conversions/L2F.java | 19 - .../jvm/instruction/conversions/L2I.java | 19 - .../jvm/instruction/extended/GOTO.java | 33 - .../jvm/instruction/extended/GOTO_W.java | 27 - .../jvm/instruction/extended/IFNONNULL.java | 33 - .../jvm/instruction/extended/IFNULL.java | 33 - .../instruction/extended/MULTIANEWARRAY.java | 25 - .../jvm/instruction/extended/WIDE.java | 40 - .../haidnor/jvm/instruction/loads/AALOAD.java | 24 - .../haidnor/jvm/instruction/loads/ALOAD.java | 25 - .../jvm/instruction/loads/ALOAD_0.java | 21 - .../jvm/instruction/loads/ALOAD_1.java | 21 - .../jvm/instruction/loads/ALOAD_2.java | 21 - .../jvm/instruction/loads/ALOAD_3.java | 21 - .../haidnor/jvm/instruction/loads/BALOAD.java | 22 - .../haidnor/jvm/instruction/loads/CALOAD.java | 22 - .../haidnor/jvm/instruction/loads/DALOAD.java | 22 - .../haidnor/jvm/instruction/loads/DLOAD.java | 25 - .../jvm/instruction/loads/DLOAD_0.java | 21 - .../jvm/instruction/loads/DLOAD_1.java | 21 - .../jvm/instruction/loads/DLOAD_2.java | 21 - .../jvm/instruction/loads/DLOAD_3.java | 21 - .../haidnor/jvm/instruction/loads/FALOAD.java | 22 - .../haidnor/jvm/instruction/loads/FLOAD.java | 25 - .../jvm/instruction/loads/FLOAD_0.java | 21 - .../jvm/instruction/loads/FLOAD_1.java | 21 - .../jvm/instruction/loads/FLOAD_2.java | 21 - .../jvm/instruction/loads/FLOAD_3.java | 21 - .../haidnor/jvm/instruction/loads/IALOAD.java | 22 - .../haidnor/jvm/instruction/loads/ILOAD.java | 25 - .../jvm/instruction/loads/ILOAD_0.java | 24 - .../jvm/instruction/loads/ILOAD_1.java | 24 - .../jvm/instruction/loads/ILOAD_2.java | 24 - .../jvm/instruction/loads/ILOAD_3.java | 24 - .../haidnor/jvm/instruction/loads/LALOAD.java | 22 - .../haidnor/jvm/instruction/loads/LLOAD.java | 25 - .../jvm/instruction/loads/LLOAD_0.java | 21 - .../jvm/instruction/loads/LLOAD_1.java | 21 - .../jvm/instruction/loads/LLOAD_2.java | 21 - .../jvm/instruction/loads/LLOAD_3.java | 21 - .../haidnor/jvm/instruction/loads/SALOAD.java | 22 - .../haidnor/jvm/instruction/math/DADD.java | 23 - .../haidnor/jvm/instruction/math/DDIV.java | 23 - .../haidnor/jvm/instruction/math/DMUL.java | 23 - .../haidnor/jvm/instruction/math/DNEG.java | 23 - .../haidnor/jvm/instruction/math/DREM.java | 23 - .../haidnor/jvm/instruction/math/DSUB.java | 23 - .../haidnor/jvm/instruction/math/FADD.java | 23 - .../haidnor/jvm/instruction/math/FDIV.java | 23 - .../haidnor/jvm/instruction/math/FMUL.java | 23 - .../haidnor/jvm/instruction/math/FNEG.java | 23 - .../haidnor/jvm/instruction/math/FREM.java | 23 - .../haidnor/jvm/instruction/math/FSUB.java | 23 - .../haidnor/jvm/instruction/math/IADD.java | 23 - .../haidnor/jvm/instruction/math/IAND.java | 21 - .../haidnor/jvm/instruction/math/IDIV.java | 23 - .../haidnor/jvm/instruction/math/IINC.java | 37 - .../haidnor/jvm/instruction/math/IMUL.java | 23 - .../haidnor/jvm/instruction/math/INEG.java | 23 - .../haidnor/jvm/instruction/math/IOR.java | 20 - .../haidnor/jvm/instruction/math/IREM.java | 23 - .../haidnor/jvm/instruction/math/ISHL.java | 22 - .../haidnor/jvm/instruction/math/ISHR.java | 22 - .../haidnor/jvm/instruction/math/ISUB.java | 23 - .../haidnor/jvm/instruction/math/IUSHR.java | 37 - .../haidnor/jvm/instruction/math/IXOR.java | 20 - .../haidnor/jvm/instruction/math/LADD.java | 23 - .../haidnor/jvm/instruction/math/LAND.java | 20 - .../haidnor/jvm/instruction/math/LDIV.java | 23 - .../haidnor/jvm/instruction/math/LMUL.java | 23 - .../haidnor/jvm/instruction/math/LNEG.java | 23 - .../haidnor/jvm/instruction/math/LOR.java | 20 - .../haidnor/jvm/instruction/math/LREM.java | 23 - .../haidnor/jvm/instruction/math/LSHL.java | 22 - .../haidnor/jvm/instruction/math/LSHR.java | 20 - .../haidnor/jvm/instruction/math/LSUB.java | 23 - .../haidnor/jvm/instruction/math/LUSHR.java | 28 - .../haidnor/jvm/instruction/math/LXOR.java | 20 - .../jvm/instruction/references/ANEWARRAY.java | 41 - .../instruction/references/ARRAYLENGTH.java | 22 - .../jvm/instruction/references/ATHROW.java | 22 - .../jvm/instruction/references/CHECKCAST.java | 24 - .../jvm/instruction/references/GETFIELD.java | 52 - .../jvm/instruction/references/GETSTATIC.java | 57 - .../instruction/references/INSTANCEOF.java | 78 - .../instruction/references/INVOKEDYNAMIC.java | 21 - .../references/INVOKEINTERFACE.java | 155 - .../instruction/references/INVOKESPECIAL.java | 87 - .../instruction/references/INVOKESTATIC.java | 97 - .../instruction/references/INVOKEVIRTUAL.java | 119 - .../jvm/instruction/references/JSR.java | 30 - .../jvm/instruction/references/JSR_W.java | 26 - .../instruction/references/MONITORENTER.java | 21 - .../instruction/references/MONITOREXIT.java | 21 - .../jvm/instruction/references/NEW.java | 44 - .../jvm/instruction/references/NEWARRAY.java | 53 - .../jvm/instruction/references/PUTFIELD.java | 35 - .../jvm/instruction/references/PUTSTATIC.java | 44 - .../haidnor/jvm/instruction/stack/DUP.java | 27 - .../haidnor/jvm/instruction/stack/DUP2.java | 41 - .../jvm/instruction/stack/DUP2_X1.java | 36 - .../jvm/instruction/stack/DUP2_X2.java | 70 - .../haidnor/jvm/instruction/stack/DUP_X1.java | 36 - .../haidnor/jvm/instruction/stack/DUP_X2.java | 47 - .../haidnor/jvm/instruction/stack/POP.java | 20 - .../haidnor/jvm/instruction/stack/POP2.java | 20 - .../haidnor/jvm/instruction/stack/SWAP.java | 24 - .../jvm/instruction/stores/AASTORE.java | 23 - .../jvm/instruction/stores/ASTORE.java | 23 - .../jvm/instruction/stores/ASTORE_0.java | 20 - .../jvm/instruction/stores/ASTORE_1.java | 20 - .../jvm/instruction/stores/ASTORE_2.java | 20 - .../jvm/instruction/stores/ASTORE_3.java | 20 - .../jvm/instruction/stores/BASTORE.java | 22 - .../jvm/instruction/stores/CASTORE.java | 22 - .../jvm/instruction/stores/DASTORE.java | 22 - .../jvm/instruction/stores/DSTORE.java | 23 - .../jvm/instruction/stores/DSTORE_0.java | 20 - .../jvm/instruction/stores/DSTORE_1.java | 20 - .../jvm/instruction/stores/DSTORE_2.java | 20 - .../jvm/instruction/stores/DSTORE_3.java | 20 - .../jvm/instruction/stores/FASTORE.java | 22 - .../jvm/instruction/stores/FSTORE.java | 23 - .../jvm/instruction/stores/FSTORE_0.java | 20 - .../jvm/instruction/stores/FSTORE_1.java | 20 - .../jvm/instruction/stores/FSTORE_2.java | 20 - .../jvm/instruction/stores/FSTORE_3.java | 20 - .../jvm/instruction/stores/IASTORE.java | 22 - .../jvm/instruction/stores/ISTORE.java | 23 - .../jvm/instruction/stores/ISTORE_0.java | 20 - .../jvm/instruction/stores/ISTORE_1.java | 20 - .../jvm/instruction/stores/ISTORE_2.java | 20 - .../jvm/instruction/stores/ISTORE_3.java | 20 - .../jvm/instruction/stores/LASTORE.java | 22 - .../jvm/instruction/stores/LSTORE.java | 23 - .../jvm/instruction/stores/LSTORE_0.java | 20 - .../jvm/instruction/stores/LSTORE_1.java | 20 - .../jvm/instruction/stores/LSTORE_2.java | 20 - .../jvm/instruction/stores/LSTORE_3.java | 20 - .../jvm/instruction/stores/SASTORE.java | 22 - .../java/haidnor/jvm/rtda/ArrayInstance.java | 15 - .../java/haidnor/jvm/rtda/BasicTypeArray.java | 65 - src/main/java/haidnor/jvm/rtda/Instance.java | 69 - .../java/haidnor/jvm/rtda/InstanceArray.java | 15 - src/main/java/haidnor/jvm/rtda/Klass.java | 120 - .../java/haidnor/jvm/rtda/KlassField.java | 52 - .../java/haidnor/jvm/rtda/KlassMethod.java | 21 - src/main/java/haidnor/jvm/rtda/Metaspace.java | 32 - .../java/haidnor/jvm/rtda/package-info.java | 17 - src/main/java/haidnor/jvm/runtime/Frame.java | 278 -- .../java/haidnor/jvm/runtime/JVMThread.java | 38 - src/main/java/haidnor/jvm/runtime/Slot.java | 24 - .../java/haidnor/jvm/runtime/StackValue.java | 26 - .../java/haidnor/jvm/util/CodeStream.java | 103 - .../haidnor/jvm/util/ConstantPoolUtil.java | 180 - src/main/java/haidnor/jvm/util/IoUtil.java | 20 - .../java/haidnor/jvm/util/JavaClassUtil.java | 25 - .../haidnor/jvm/util/JvmThreadHolder.java | 20 - .../java/haidnor/jvm/util/SignatureUtil.java | 37 - src/test/java/haidnor/jvm/test/BCELTest.java | 15 + src/test/java/haidnor/jvm/test/TestJVM.java | 190 - .../java/haidnor/jvm/test/clazz/Func.java | 7 - .../java/haidnor/jvm/test/clazz/Human.java | 16 - .../java/haidnor/jvm/test/clazz/Organism.java | 8 - .../haidnor/jvm/test/clazz/Organism0.java | 12 - .../java/haidnor/jvm/test/clazz/Student.java | 19 - .../haidnor/jvm/test/clazz/StudentEnum.java | 23 - .../java/haidnor/jvm/test/demo/Demo2.java | 12 - .../java/haidnor/jvm/test/demo/Demo3.java | 22 - .../java/haidnor/jvm/test/demo/Demo4.java | 12 - .../java/haidnor/jvm/test/demo/Demo5.java | 27 - .../java/haidnor/jvm/test/demo/Demo6.java | 16 - .../java/haidnor/jvm/test/demo/Demo7.java | 20 - .../java/haidnor/jvm/test/demo/Demo8.java | 17 - .../haidnor/jvm/test/demo/demo_doWhile.java | 27 - .../haidnor/jvm/test/demo/demo_enum_1.java | 11 - .../jvm/test/demo/demo_exception_1.java | 37 - .../jvm/test/demo/demo_exception_2.java | 53 - .../jvm/test/demo/demo_exception_3.java | 50 - .../jvm/test/demo/demo_exception_4.java | 50 - .../haidnor/jvm/test/demo/demo_finally_1.java | 51 - .../haidnor/jvm/test/demo/demo_finally_2.java | 55 - .../haidnor/jvm/test/demo/demo_finally_3.java | 55 - .../java/haidnor/jvm/test/demo/demo_for.java | 31 - .../haidnor/jvm/test/demo/demo_foreach_1.java | 62 - .../haidnor/jvm/test/demo/demo_foreach_2.java | 25 - .../haidnor/jvm/test/demo/demo_foreach_3.java | 20 - .../jvm/test/demo/demo_helloWorld.java | 18 - .../haidnor/jvm/test/demo/demo_while.java | 29 - .../haidnor/jvm/test/instruction/Array.java | 10 - .../haidnor/jvm/test/instruction/ClassA.java | 12 - .../jvm/test/instruction/DO_WHILE.java | 11 - .../haidnor/jvm/test/instruction/GOTO.java | 13 - .../jvm/test/instruction/InnerClass.java | 9 - .../jvm/test/instruction/loads/Person.java | 4 - .../jvm/test/instruction/loads/Student.java | 31 - .../jvm/test/instruction/loads/Student1.java | 13 - .../jvm/test/instruction/math/DADD.java | 15 - .../jvm/test/instruction/math/FADD.java | 15 - .../jvm/test/instruction/math/IADD.java | 15 - .../jvm/test/instruction/math/IINC.java | 23 - .../jvm/test/instruction/math/ISUB.java | 10 - .../jvm/test/instruction/math/LADD.java | 15 - .../jvm/test/instruction/math/LSUB.java | 10 - .../references/CallStaticMethod.java | 14 - .../jvm/test/instruction/references/NEW.java | 21 - 601 files changed, 43626 insertions(+), 9053 deletions(-) delete mode 100644 src/main/java/haidnor/jvm/Main.java create mode 100644 src/main/java/haidnor/jvm/bcel/Const.java create mode 100644 src/main/java/haidnor/jvm/bcel/ExceptionConst.java create mode 100644 src/main/java/haidnor/jvm/bcel/Repository.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/AccessFlags.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/AnnotationDefault.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/AnnotationElementValue.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/AnnotationEntry.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Annotations.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ArrayElementValue.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Attribute.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/BootstrapMethod.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/BootstrapMethods.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ClassElementValue.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ClassFormatException.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ClassParser.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Code.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/CodeException.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Constant.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantCP.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantClass.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantDouble.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantDynamic.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantFieldref.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantFloat.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantInteger.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantInterfaceMethodref.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantInvokeDynamic.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantLong.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodHandle.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodType.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodref.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantModule.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantNameAndType.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantObject.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantPackage.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantPool.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantString.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantUtf8.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ConstantValue.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Deprecated.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/DescendingVisitor.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ElementValue.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ElementValuePair.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/EmptyVisitor.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/EnclosingMethod.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/EnumElementValue.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ExceptionTable.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/FieldOrMethod.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/InnerClass.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/InnerClasses.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/JavaClass.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/JavaField.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/JavaMethod.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/LineNumber.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/LineNumberTable.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/LocalVariable.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/LocalVariableTable.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/LocalVariableTypeTable.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/MethodParameter.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/MethodParameters.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Module.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ModuleExports.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ModuleMainClass.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ModuleOpens.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ModulePackages.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ModuleProvides.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ModuleRequires.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/NestHost.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/NestMembers.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Node.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/PMGClass.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ParameterAnnotationEntry.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/ParameterAnnotations.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/RuntimeInvisibleAnnotations.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/RuntimeInvisibleParameterAnnotations.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/RuntimeVisibleAnnotations.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/RuntimeVisibleParameterAnnotations.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Signature.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/SimpleElementValue.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/SourceFile.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/StackMap.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/StackMapEntry.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/StackMapType.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Synthetic.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Unknown.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/UnknownAttributeReader.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Utility.java create mode 100644 src/main/java/haidnor/jvm/bcel/classfile/Visitor.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/AALOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/AASTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ACONST_NULL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ALOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ANEWARRAY.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ARETURN.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ARRAYLENGTH.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ASTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ATHROW.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/AllocationInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/AnnotationElementValueGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/AnnotationEntryGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ArithmeticInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ArrayElementValueGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ArrayInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ArrayType.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/BALOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/BASTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/BIPUSH.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/BREAKPOINT.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/BasicType.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/BranchHandle.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/BranchInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/CALOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/CASTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/CHECKCAST.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/CPInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ClassElementValueGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ClassGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ClassGenException.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ClassObserver.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/CodeExceptionGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/CompoundInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ConstantPoolGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ConstantPushInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ConversionInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/D2F.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/D2I.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/D2L.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DADD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DALOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DASTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DCMPG.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DCMPL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DCONST.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DDIV.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DLOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DMUL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DNEG.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DREM.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DRETURN.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DSTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DSUB.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DUP.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DUP2.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DUP2_X1.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DUP2_X2.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DUP_X1.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/DUP_X2.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ElementValueGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ElementValuePairGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/EmptyVisitor.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/EnumElementValueGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ExceptionThrower.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/F2D.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/F2I.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/F2L.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FADD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FALOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FASTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FCMPG.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FCMPL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FCONST.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FDIV.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FLOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FMUL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FNEG.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FREM.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FRETURN.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FSTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FSUB.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FieldGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FieldGenOrMethodGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FieldInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FieldObserver.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/FieldOrMethod.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/GETFIELD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/GETSTATIC.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/GOTO.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/GOTO_W.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/GotoInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/I2B.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/I2C.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/I2D.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/I2F.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/I2L.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/I2S.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IADD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IALOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IAND.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IASTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ICONST.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IDIV.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IFEQ.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IFGE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IFGT.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IFLE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IFLT.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IFNE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IFNONNULL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IFNULL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IF_ACMPEQ.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IF_ACMPNE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IF_ICMPEQ.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IF_ICMPGE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IF_ICMPGT.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IF_ICMPLE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IF_ICMPLT.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IF_ICMPNE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IINC.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ILOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IMPDEP1.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IMPDEP2.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IMUL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/INEG.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/INSTANCEOF.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/INVOKEDYNAMIC.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/INVOKEINTERFACE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/INVOKESPECIAL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/INVOKESTATIC.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/INVOKEVIRTUAL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IOR.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IREM.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IRETURN.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ISHL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ISHR.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ISTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ISUB.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IUSHR.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IXOR.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IfInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/IndexedInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/Instruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/InstructionComparator.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/InstructionConst.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/InstructionFactory.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/InstructionHandle.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/InstructionList.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/InstructionListObserver.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/InstructionTargeter.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/InvokeInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/JSR.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/JSR_W.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/JsrInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/L2D.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/L2F.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/L2I.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LADD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LALOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LAND.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LASTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LCMP.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LCONST.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LDC.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LDC2_W.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LDC_W.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LDIV.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LLOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LMUL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LNEG.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LOOKUPSWITCH.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LOR.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LREM.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LRETURN.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LSHL.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LSHR.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LSTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LSUB.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LUSHR.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LXOR.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LineNumberGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LoadClass.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LoadInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LocalVariableGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/LocalVariableInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/MONITORENTER.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/MONITOREXIT.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/MULTIANEWARRAY.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/MethodGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/MethodObserver.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/NEW.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/NEWARRAY.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/NOP.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/NameSignatureInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/NamedAndTyped.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ObjectType.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/POP.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/POP2.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/PUSH.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/PUTFIELD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/PUTSTATIC.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/PopInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/PushInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/RET.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/RETURN.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ReferenceType.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ReturnInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/ReturnaddressType.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/SALOAD.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/SASTORE.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/SIPUSH.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/SWAP.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/SWITCH.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/Select.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/SimpleElementValueGen.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/StackConsumer.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/StackInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/StackProducer.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/StoreInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/TABLESWITCH.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/TargetLostException.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/Type.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/TypedInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/UnconditionalBranch.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/VariableLengthInstruction.java create mode 100644 src/main/java/haidnor/jvm/bcel/generic/Visitor.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/AbstractClassPathRepository.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/Args.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/BCELComparator.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/BCELFactory.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/BCELifier.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/ByteSequence.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/ClassPath.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/ClassQueue.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/JavaWrapper.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/MemorySensitiveClassPathRepository.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/ModularRuntimeImage.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/Repository.java create mode 100644 src/main/java/haidnor/jvm/bcel/util/SyntheticRepository.java delete mode 100644 src/main/java/haidnor/jvm/classloader/ClassLoader.java delete mode 100644 src/main/java/haidnor/jvm/core/JavaExecutionEngine.java delete mode 100644 src/main/java/haidnor/jvm/instruction/Instruction.java delete mode 100644 src/main/java/haidnor/jvm/instruction/InstructionFactory.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/DCMPG.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/DCMPL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/FCMPG.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/FCMPL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IFEQ.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IFGE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IFGT.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IFLE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IFLT.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IFNE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IF_ACMPEQ.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IF_ACMPNE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPEQ.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPGE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPGT.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPLE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPLT.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPNE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/comparisons/LCMP.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/ACONST_NULL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/BIPUSH.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/DCONST_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/DCONST_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/FCONST_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/FCONST_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/ICONST_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/ICONST_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/ICONST_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/ICONST_3.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/ICONST_4.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/ICONST_5.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/ICONST_M1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/LCONST_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/LCONST_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/LDC.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/LDC2W.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/LDC_W.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/NOP.java delete mode 100644 src/main/java/haidnor/jvm/instruction/constants/SIPUSH.java delete mode 100644 src/main/java/haidnor/jvm/instruction/control/ARETURN.java delete mode 100644 src/main/java/haidnor/jvm/instruction/control/DRETURN.java delete mode 100644 src/main/java/haidnor/jvm/instruction/control/FRETURN.java delete mode 100644 src/main/java/haidnor/jvm/instruction/control/IRETURN.java delete mode 100644 src/main/java/haidnor/jvm/instruction/control/LOOKUPSWITCH.java delete mode 100644 src/main/java/haidnor/jvm/instruction/control/LRETURN.java delete mode 100644 src/main/java/haidnor/jvm/instruction/control/RET.java delete mode 100644 src/main/java/haidnor/jvm/instruction/control/RETURN.java delete mode 100644 src/main/java/haidnor/jvm/instruction/control/ReturnableInstruction.java delete mode 100644 src/main/java/haidnor/jvm/instruction/control/TABLESWITCH.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/D2F.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/D2I.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/D2L.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/F2D.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/F2I.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/F2L.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/I2B.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/I2C.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/I2D.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/I2F.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/I2L.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/I2S.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/L2D.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/L2F.java delete mode 100644 src/main/java/haidnor/jvm/instruction/conversions/L2I.java delete mode 100644 src/main/java/haidnor/jvm/instruction/extended/GOTO.java delete mode 100644 src/main/java/haidnor/jvm/instruction/extended/GOTO_W.java delete mode 100644 src/main/java/haidnor/jvm/instruction/extended/IFNONNULL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/extended/IFNULL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/extended/MULTIANEWARRAY.java delete mode 100644 src/main/java/haidnor/jvm/instruction/extended/WIDE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/AALOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/ALOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/ALOAD_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/ALOAD_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/ALOAD_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/ALOAD_3.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/BALOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/CALOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/DALOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/DLOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/DLOAD_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/DLOAD_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/DLOAD_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/DLOAD_3.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/FALOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/FLOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/FLOAD_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/FLOAD_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/FLOAD_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/FLOAD_3.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/IALOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/ILOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/ILOAD_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/ILOAD_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/ILOAD_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/ILOAD_3.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/LALOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/LLOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/LLOAD_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/LLOAD_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/LLOAD_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/LLOAD_3.java delete mode 100644 src/main/java/haidnor/jvm/instruction/loads/SALOAD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/DADD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/DDIV.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/DMUL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/DNEG.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/DREM.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/DSUB.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/FADD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/FDIV.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/FMUL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/FNEG.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/FREM.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/FSUB.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/IADD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/IAND.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/IDIV.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/IINC.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/IMUL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/INEG.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/IOR.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/IREM.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/ISHL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/ISHR.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/ISUB.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/IUSHR.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/IXOR.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LADD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LAND.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LDIV.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LMUL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LNEG.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LOR.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LREM.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LSHL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LSHR.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LSUB.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LUSHR.java delete mode 100644 src/main/java/haidnor/jvm/instruction/math/LXOR.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/ANEWARRAY.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/ARRAYLENGTH.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/ATHROW.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/CHECKCAST.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/GETFIELD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/GETSTATIC.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/INSTANCEOF.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/INVOKEDYNAMIC.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/INVOKEINTERFACE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/INVOKESPECIAL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/INVOKESTATIC.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/INVOKEVIRTUAL.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/JSR.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/JSR_W.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/MONITORENTER.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/MONITOREXIT.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/NEW.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/NEWARRAY.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/PUTFIELD.java delete mode 100644 src/main/java/haidnor/jvm/instruction/references/PUTSTATIC.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stack/DUP.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stack/DUP2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stack/DUP2_X1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stack/DUP2_X2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stack/DUP_X1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stack/DUP_X2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stack/POP.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stack/POP2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stack/SWAP.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/AASTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/ASTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/ASTORE_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/ASTORE_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/ASTORE_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/ASTORE_3.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/BASTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/CASTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/DASTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/DSTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/DSTORE_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/DSTORE_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/DSTORE_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/DSTORE_3.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/FASTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/FSTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/FSTORE_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/FSTORE_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/FSTORE_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/FSTORE_3.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/IASTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/ISTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/ISTORE_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/ISTORE_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/ISTORE_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/ISTORE_3.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/LASTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/LSTORE.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/LSTORE_0.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/LSTORE_1.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/LSTORE_2.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/LSTORE_3.java delete mode 100644 src/main/java/haidnor/jvm/instruction/stores/SASTORE.java delete mode 100644 src/main/java/haidnor/jvm/rtda/ArrayInstance.java delete mode 100644 src/main/java/haidnor/jvm/rtda/BasicTypeArray.java delete mode 100644 src/main/java/haidnor/jvm/rtda/Instance.java delete mode 100644 src/main/java/haidnor/jvm/rtda/InstanceArray.java delete mode 100644 src/main/java/haidnor/jvm/rtda/Klass.java delete mode 100644 src/main/java/haidnor/jvm/rtda/KlassField.java delete mode 100644 src/main/java/haidnor/jvm/rtda/KlassMethod.java delete mode 100644 src/main/java/haidnor/jvm/rtda/Metaspace.java delete mode 100644 src/main/java/haidnor/jvm/rtda/package-info.java delete mode 100644 src/main/java/haidnor/jvm/runtime/Frame.java delete mode 100644 src/main/java/haidnor/jvm/runtime/JVMThread.java delete mode 100644 src/main/java/haidnor/jvm/runtime/Slot.java delete mode 100644 src/main/java/haidnor/jvm/runtime/StackValue.java delete mode 100644 src/main/java/haidnor/jvm/util/CodeStream.java delete mode 100644 src/main/java/haidnor/jvm/util/ConstantPoolUtil.java delete mode 100644 src/main/java/haidnor/jvm/util/IoUtil.java delete mode 100644 src/main/java/haidnor/jvm/util/JavaClassUtil.java delete mode 100644 src/main/java/haidnor/jvm/util/JvmThreadHolder.java delete mode 100644 src/main/java/haidnor/jvm/util/SignatureUtil.java create mode 100644 src/test/java/haidnor/jvm/test/BCELTest.java delete mode 100644 src/test/java/haidnor/jvm/test/TestJVM.java delete mode 100644 src/test/java/haidnor/jvm/test/clazz/Func.java delete mode 100644 src/test/java/haidnor/jvm/test/clazz/Human.java delete mode 100644 src/test/java/haidnor/jvm/test/clazz/Organism.java delete mode 100644 src/test/java/haidnor/jvm/test/clazz/Organism0.java delete mode 100644 src/test/java/haidnor/jvm/test/clazz/Student.java delete mode 100644 src/test/java/haidnor/jvm/test/clazz/StudentEnum.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/Demo2.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/Demo3.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/Demo4.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/Demo5.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/Demo6.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/Demo7.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/Demo8.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_doWhile.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_enum_1.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_exception_1.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_exception_2.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_exception_3.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_exception_4.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_finally_1.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_finally_2.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_finally_3.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_for.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_foreach_1.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_foreach_2.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_foreach_3.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_helloWorld.java delete mode 100644 src/test/java/haidnor/jvm/test/demo/demo_while.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/Array.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/ClassA.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/DO_WHILE.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/GOTO.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/InnerClass.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/loads/Person.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/loads/Student.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/loads/Student1.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/math/DADD.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/math/FADD.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/math/IADD.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/math/IINC.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/math/ISUB.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/math/LADD.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/math/LSUB.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/references/CallStaticMethod.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/references/NEW.java diff --git a/pom.xml b/pom.xml index a962a0d..166d3df 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,13 @@ + + + org.apache.commons + commons-lang3 + 3.12.0 + + commons-cli @@ -22,20 +29,6 @@ 1.5.0 - - - org.apache.bcel - bcel - 6.7.0 - - - - org.projectlombok - lombok - 1.18.26 - provided - - org.slf4j diff --git a/src/main/java/haidnor/jvm/Main.java b/src/main/java/haidnor/jvm/Main.java deleted file mode 100644 index cff1a2c..0000000 --- a/src/main/java/haidnor/jvm/Main.java +++ /dev/null @@ -1,91 +0,0 @@ -package haidnor.jvm; - -import org.apache.commons.cli.*; - -import haidnor.jvm.classloader.ClassLoader; -import haidnor.jvm.core.JavaExecutionEngine; -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.KlassMethod; -import haidnor.jvm.rtda.Metaspace; -import haidnor.jvm.runtime.JVMThread; -import haidnor.jvm.util.JavaClassUtil; -import haidnor.jvm.util.JvmThreadHolder; -import lombok.SneakyThrows; - -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -/** - * @author wang xiang - */ -public class Main { - - @SneakyThrows - public static void main(String[] args) { - String banner = """ - ██╗ ██╗ █████╗ ██╗██████╗ ███╗ ██╗ ██████╗ ██████╗ ██╗██╗ ██╗███╗ ███╗ - ██║ ██║██╔══██╗██║██╔══██╗████╗ ██║██╔═══██╗██╔══██╗ ██║██║ ██║████╗ ████║ - ███████║███████║██║██║ ██║██╔██╗ ██║██║ ██║██████╔╝ ██║██║ ██║██╔████╔██║ - ██╔══██║██╔══██║██║██║ ██║██║╚██╗██║██║ ██║██╔══██╗ ██ ██║╚██╗ ██╔╝██║╚██╔╝██║ - ██║ ██║██║ ██║██║██████╔╝██║ ╚████║╚██████╔╝██║ ██║ ╚█████╔╝ ╚████╔╝ ██║ ╚═╝ ██║ - ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚════╝ ╚═══╝ ╚═╝ ╚═╝ - """; - - System.out.println(banner); - - CommandLine cmd = initCommandLine(args); - - // 指定从 .jar 文件运行 - if (cmd.hasOption("jar")) { - String jarFilePath = cmd.getOptionValue("jar"); - try (JarFile jarFile = new JarFile(jarFilePath)) { - ClassLoader bootClassLoader = new ClassLoader(jarFile, "ApplicationClassLoader"); - String mainClass = jarFile.getManifest().getMainAttributes().getValue("Main-Class"); - - Enumeration entries = jarFile.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - if (!entry.isDirectory() && entry.getName().endsWith(".class")) { - String className = entry.getName().replace('/', '.').substring(0, entry.getName().length() - 6); - if (className.equals(mainClass)) { - JvmThreadHolder.set(new JVMThread()); - Klass mainMeteKlass = bootClassLoader.loadClass(jarFile, entry); - KlassMethod mainKlassMethod = JavaClassUtil.getMainMethod(mainMeteKlass); - Metaspace.registerJavaClass(mainMeteKlass); - JavaExecutionEngine.callMainMethod(mainKlassMethod); - return; - } - } - } - } - } - - // 指定从 .class 文件运行 - if (cmd.hasOption("class")) { - JvmThreadHolder.set(new JVMThread()); - String path = cmd.getOptionValue("class"); - ClassLoader bootClassLoader = new ClassLoader("ApplicationClassLoader"); - Klass mainMeteKlass = bootClassLoader.loadClassWithAbsolutePath(path); - KlassMethod mainKlassMethod = JavaClassUtil.getMainMethod(mainMeteKlass); - Metaspace.registerJavaClass(mainMeteKlass); - - JavaExecutionEngine.callMainMethod(mainKlassMethod); - } - } - - private static CommandLine initCommandLine(String[] args) throws ParseException { - Option jarOption = new Option("jar", true, "运行 jar 程序"); - Option classOption = new Option("class", true, "运行 .class 字节码文件"); - - OptionGroup optionGroup = new OptionGroup(); - optionGroup.addOption(jarOption).addOption(classOption); - - Options options = new Options(); - options.addOptionGroup(optionGroup); - - CommandLineParser parser = new DefaultParser(); - return parser.parse(options, args); - } - -} \ No newline at end of file diff --git a/src/main/java/haidnor/jvm/bcel/Const.java b/src/main/java/haidnor/jvm/bcel/Const.java new file mode 100644 index 0000000..3abecf1 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/Const.java @@ -0,0 +1,3234 @@ +/* + * 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 haidnor.jvm.bcel; + +import java.util.Arrays; +import java.util.Collections; + +/** + * Constants for the project, mostly defined in the JVM specification. + * + * @since 6.0 (intended to replace the Constants interface) + */ +public final class Const { + + /** + * Java class file format Magic number (0xCAFEBABE) + * + * @see The ClassFile Structure + * in The Java Virtual Machine Specification + */ + public static final int JVM_CLASSFILE_MAGIC = 0xCAFEBABE; + + /** + * Major version number of class files for Java 1.1. + * + * @see #MINOR_1_1 + */ + public static final short MAJOR_1_1 = 45; + + /** + * Minor version number of class files for Java 1.1. + * + * @see #MAJOR_1_1 + */ + public static final short MINOR_1_1 = 3; + + /** + * Major version number of class files for Java 1.2. + * + * @see #MINOR_1_2 + */ + public static final short MAJOR_1_2 = 46; + + /** + * Minor version number of class files for Java 1.2. + * + * @see #MAJOR_1_2 + */ + public static final short MINOR_1_2 = 0; + + /** + * Major version number of class files for Java 1.2. + * + * @see #MINOR_1_2 + */ + public static final short MAJOR_1_3 = 47; + + /** + * Minor version number of class files for Java 1.3. + * + * @see #MAJOR_1_3 + */ + public static final short MINOR_1_3 = 0; + + /** + * Major version number of class files for Java 1.3. + * + * @see #MINOR_1_3 + */ + public static final short MAJOR_1_4 = 48; + + /** + * Minor version number of class files for Java 1.4. + * + * @see #MAJOR_1_4 + */ + public static final short MINOR_1_4 = 0; + + /** + * Major version number of class files for Java 1.4. + * + * @see #MINOR_1_4 + */ + public static final short MAJOR_1_5 = 49; + + /** + * Minor version number of class files for Java 1.5. + * + * @see #MAJOR_1_5 + */ + public static final short MINOR_1_5 = 0; + + /** + * Major version number of class files for Java 1.6. + * + * @see #MINOR_1_6 + */ + public static final short MAJOR_1_6 = 50; + + /** + * Minor version number of class files for Java 1.6. + * + * @see #MAJOR_1_6 + */ + public static final short MINOR_1_6 = 0; + + /** + * Major version number of class files for Java 1.7. + * + * @see #MINOR_1_7 + */ + public static final short MAJOR_1_7 = 51; + + /** + * Minor version number of class files for Java 1.7. + * + * @see #MAJOR_1_7 + */ + public static final short MINOR_1_7 = 0; + + /** + * Major version number of class files for Java 1.8. + * + * @see #MINOR_1_8 + */ + public static final short MAJOR_1_8 = 52; + + /** + * Minor version number of class files for Java 1.8. + * + * @see #MAJOR_1_8 + */ + public static final short MINOR_1_8 = 0; + + /** + * Major version number of class files for Java 9. + * + * @see #MINOR_9 + */ + public static final short MAJOR_9 = 53; + + /** + * Minor version number of class files for Java 9. + * + * @see #MAJOR_9 + */ + public static final short MINOR_9 = 0; + + /** + * @deprecated Use {@link #MAJOR_9} instead + */ + @Deprecated + public static final short MAJOR_1_9 = MAJOR_9; + + /** + * @deprecated Use {@link #MINOR_9} instead + */ + @Deprecated + public static final short MINOR_1_9 = MINOR_9; + + /** + * Major version number of class files for Java 10. + * + * @see #MINOR_10 + */ + public static final short MAJOR_10 = 54; + + /** + * Minor version number of class files for Java 10. + * + * @see #MAJOR_10 + */ + public static final short MINOR_10 = 0; + + /** + * Major version number of class files for Java 11. + * + * @see #MINOR_11 + */ + public static final short MAJOR_11 = 55; + + /** + * Minor version number of class files for Java 11. + * + * @see #MAJOR_11 + */ + public static final short MINOR_11 = 0; + + /** + * Major version number of class files for Java 12. + * + * @see #MINOR_12 + */ + public static final short MAJOR_12 = 56; + + /** + * Minor version number of class files for Java 12. + * + * @see #MAJOR_12 + */ + public static final short MINOR_12 = 0; + + /** + * Major version number of class files for Java 13. + * + * @see #MINOR_13 + */ + public static final short MAJOR_13 = 57; + + /** + * Minor version number of class files for Java 13. + * + * @see #MAJOR_13 + */ + public static final short MINOR_13 = 0; + + /** + * Minor version number of class files for Java 14. + * + * @see #MAJOR_14 + * @since 6.4.0 + */ + public static final short MINOR_14 = 0; + + /** + * Minor version number of class files for Java 15. + * + * @see #MAJOR_15 + * @since 6.6.0 + */ + public static final short MINOR_15 = 0; + + /** + * Minor version number of class files for Java 16. + * + * @see #MAJOR_16 + * @since 6.6.0 + */ + public static final short MINOR_16 = 0; + + /** + * Minor version number of class files for Java 17. + * + * @see #MAJOR_17 + * @since 6.6.0 + */ + public static final short MINOR_17 = 0; + + /** + * Minor version number of class files for Java 18. + * + * @see #MAJOR_18 + * @since 6.6.0 + */ + public static final short MINOR_18 = 0; + + /** + * Minor version number of class files for Java 19. + * + * @see #MAJOR_19 + * @since 6.6.0 + */ + public static final short MINOR_19 = 0; + + /** + * Major version number of class files for Java 14. + * + * @see #MINOR_14 + * @since 6.4.0 + */ + public static final short MAJOR_14 = 58; + + /** + * Major version number of class files for Java 15. + * + * @see #MINOR_15 + * @since 6.6.0 + */ + public static final short MAJOR_15 = 59; + + /** + * Major version number of class files for Java 16. + * + * @see #MINOR_16 + * @since 6.6.0 + */ + public static final short MAJOR_16 = 60; + + /** + * Major version number of class files for Java 17. + * + * @see #MINOR_17 + * @since 6.6.0 + */ + public static final short MAJOR_17 = 61; + + /** + * Major version number of class files for Java 18. + * + * @see #MINOR_18 + * @since 6.6.0 + */ + public static final short MAJOR_18 = 62; + + /** + * Major version number of class files for Java 19. + * + * @see #MINOR_19 + * @since 6.6.0 + */ + public static final short MAJOR_19 = 63; + + /** + * Default major version number. Class file is for Java 1.1. + * + * @see #MAJOR_1_1 + */ + public static final short MAJOR = MAJOR_1_1; + + /** + * Default major version number. Class file is for Java 1.1. + * + * @see #MAJOR_1_1 + */ + public static final short MINOR = MINOR_1_1; + + /** + * Maximum value for an unsigned short. + */ + public static final int MAX_SHORT = 65535; // 2^16 - 1 + + /** + * Maximum value for an unsigned byte. + */ + public static final int MAX_BYTE = 255; // 2^8 - 1 + + /** + * One of the access flags for fields, methods, or classes. + * + * @see Flag definitions for + * Classes in the Java Virtual Machine Specification (Java SE 9 Edition). + * @see Flag definitions for Fields + * in the Java Virtual Machine Specification (Java SE 9 Edition). + * @see Flag definitions for Methods + * in the Java Virtual Machine Specification (Java SE 9 Edition). + * @see Flag + * definitions for Inner Classes in the Java Virtual Machine Specification (Java SE 9 Edition). + */ + public static final short ACC_PUBLIC = 0x0001; + + /** + * One of the access flags for fields, methods, or classes. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_PRIVATE = 0x0002; + + /** + * One of the access flags for fields, methods, or classes. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_PROTECTED = 0x0004; + + /** + * One of the access flags for fields, methods, or classes. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_STATIC = 0x0008; + + /** + * One of the access flags for fields, methods, or classes. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_FINAL = 0x0010; + + /** + * One of the access flags for the Module attribute. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_OPEN = 0x0020; + + /** + * One of the access flags for classes. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_SUPER = 0x0020; + + /** + * One of the access flags for methods. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_SYNCHRONIZED = 0x0020; + + /** + * One of the access flags for the Module attribute. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_TRANSITIVE = 0x0020; + + /** + * One of the access flags for methods. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_BRIDGE = 0x0040; + + /** + * One of the access flags for the Module attribute. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_STATIC_PHASE = 0x0040; + + /** + * One of the access flags for fields. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_VOLATILE = 0x0040; + + /** + * One of the access flags for fields. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_TRANSIENT = 0x0080; + + /** + * One of the access flags for methods. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_VARARGS = 0x0080; + + /** + * One of the access flags for methods. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_NATIVE = 0x0100; + + /** + * One of the access flags for classes. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_INTERFACE = 0x0200; + + /** + * One of the access flags for methods or classes. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_ABSTRACT = 0x0400; + + /** + * One of the access flags for methods. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_STRICT = 0x0800; + + /** + * One of the access flags for fields, methods, classes, MethodParameter attribute, or Module attribute. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_SYNTHETIC = 0x1000; + + /** + * One of the access flags for classes. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_ANNOTATION = 0x2000; + + /** + * One of the access flags for fields or classes. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_ENUM = 0x4000; + + // Applies to classes compiled by new compilers only + /** + * One of the access flags for MethodParameter or Module attributes. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_MANDATED = (short) 0x8000; + + /** + * One of the access flags for classes. + * + * @see #ACC_PUBLIC + */ + public static final short ACC_MODULE = (short) 0x8000; + + /** + * One of the access flags for fields, methods, or classes. + * + * @see #ACC_PUBLIC + * @deprecated Use {@link #MAX_ACC_FLAG_I} + */ + @Deprecated + public static final short MAX_ACC_FLAG = ACC_ENUM; + + /** + * One of the access flags for fields, methods, or classes. ACC_MODULE is negative as a short. + * + * @see #ACC_PUBLIC + * @since 6.4.0 + */ + public static final int MAX_ACC_FLAG_I = 0x8000; // ACC_MODULE is negative as a short + + // Note that do to overloading: + // 'synchronized' is for methods, might be 'open' (if Module), 'super' (if class), or 'transitive' (if Module). + // 'volatile' is for fields, might be 'bridge' (if method) or 'static_phase' (if Module) + // 'transient' is for fields, might be 'varargs' (if method) + // 'module' is for classes, might be 'mandated' (if Module or MethodParameters) + /** + * The names of the access flags. + */ + private static final String[] ACCESS_NAMES = {"public", "private", "protected", "static", "final", "synchronized", "volatile", "transient", "native", + "interface", "abstract", "strictfp", "synthetic", "annotation", "enum", "module"}; + + /** @since 6.0 */ + public static final int ACCESS_NAMES_LENGTH = ACCESS_NAMES.length; + + /** + * Marks a constant pool entry as type UTF-8. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_Utf8 = 1; + + /* + * The description of the constant pool is at: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4 + * References below are to the individual sections + */ + + /** + * Marks a constant pool entry as type Integer. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_Integer = 3; + + /** + * Marks a constant pool entry as type Float. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_Float = 4; + + /** + * Marks a constant pool entry as type Long. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_Long = 5; + + /** + * Marks a constant pool entry as type Double. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_Double = 6; + + /** + * Marks a constant pool entry as a Class + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_Class = 7; + + /** + * Marks a constant pool entry as a Field Reference. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_Fieldref = 9; + + /** + * Marks a constant pool entry as type String + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_String = 8; + + /** + * Marks a constant pool entry as a Method Reference. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_Methodref = 10; + + /** + * Marks a constant pool entry as an Interface Method Reference. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_InterfaceMethodref = 11; + + /** + * Marks a constant pool entry as a name and type. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_NameAndType = 12; + + /** + * Marks a constant pool entry as a Method Handle. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_MethodHandle = 15; + + /** + * Marks a constant pool entry as a Method Type. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_MethodType = 16; + + /** + * Marks a constant pool entry as dynamically computed. + * + * @see Change request for JEP + * 309 + * @since 6.3 + */ + public static final byte CONSTANT_Dynamic = 17; + + /** + * Marks a constant pool entry as an Invoke Dynamic + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + */ + public static final byte CONSTANT_InvokeDynamic = 18; + + /** + * Marks a constant pool entry as a Module Reference. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + * @since 6.1 + */ + public static final byte CONSTANT_Module = 19; + + /** + * Marks a constant pool entry as a Package Reference. + * + * @see The Constant Pool in The + * Java Virtual Machine Specification + * @since 6.1 + */ + public static final byte CONSTANT_Package = 20; + + /** + * The names of the types of entries in a constant pool. Use getConstantName instead + */ + private static final String[] CONSTANT_NAMES = {"", "CONSTANT_Utf8", "", "CONSTANT_Integer", "CONSTANT_Float", "CONSTANT_Long", "CONSTANT_Double", + "CONSTANT_Class", "CONSTANT_String", "CONSTANT_Fieldref", "CONSTANT_Methodref", "CONSTANT_InterfaceMethodref", "CONSTANT_NameAndType", "", "", + "CONSTANT_MethodHandle", "CONSTANT_MethodType", "CONSTANT_Dynamic", "CONSTANT_InvokeDynamic", "CONSTANT_Module", "CONSTANT_Package"}; + + /** + * The name of the static initializer, also called "class initialization method" or "interface + * initialization method". This is "<clinit>". + */ + public static final String STATIC_INITIALIZER_NAME = ""; + + /** + * The name of every constructor method in a class, also called "instance initialization method". This is + * "<init>". + */ + public static final String CONSTRUCTOR_NAME = ""; + + /** + * The names of the interfaces implemented by arrays + */ + private static final String[] INTERFACES_IMPLEMENTED_BY_ARRAYS = {"java.lang.Cloneable", "java.io.Serializable"}; + + /** + * Maximum Constant Pool entries. One of the limitations of the Java Virtual Machine. + * + * @see The Java Virtual + * Machine Specification, Java SE 8 Edition, page 330, chapter 4.11. + */ + public static final int MAX_CP_ENTRIES = 65535; + + /** + * Maximum code size (plus one; the code size must be LESS than this) One of the limitations of the Java Virtual + * Machine. Note vmspec2 page 152 ("Limitations") says: "The amount of code per non-native, non-abstract method is + * limited to 65536 bytes by the sizes of the indices in the exception_table of the Code attribute (§4.7.3), in the + * LineNumberTable attribute (§4.7.8), and in the LocalVariableTable attribute (§4.7.9)." However this should be taken + * as an upper limit rather than the defined maximum. On page 134 (4.8.1 Static Constants) of the same spec, it says: + * "The value of the code_length item must be less than 65536." The entry in the Limitations section has been removed + * from later versions of the spec; it is not present in the Java SE 8 edition. + * + * @see The Java Virtual + * Machine Specification, Java SE 8 Edition, page 104, chapter 4.7. + */ + public static final int MAX_CODE_SIZE = 65536; // bytes + + /** + * The maximum number of dimensions in an array ({@value}). One of the limitations of the Java Virtual Machine. + * + * @see Field Descriptors in + * The Java Virtual Machine Specification + */ + public static final int MAX_ARRAY_DIMENSIONS = 255; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short NOP = 0; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short ACONST_NULL = 1; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ICONST_M1 = 2; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ICONST_0 = 3; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ICONST_1 = 4; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ICONST_2 = 5; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ICONST_3 = 6; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ICONST_4 = 7; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ICONST_5 = 8; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short LCONST_0 = 9; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short LCONST_1 = 10; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short FCONST_0 = 11; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short FCONST_1 = 12; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short FCONST_2 = 13; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short DCONST_0 = 14; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short DCONST_1 = 15; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short BIPUSH = 16; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short SIPUSH = 17; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short LDC = 18; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LDC_W = 19; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LDC2_W = 20; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ILOAD = 21; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LLOAD = 22; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FLOAD = 23; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DLOAD = 24; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ALOAD = 25; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ILOAD_0 = 26; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ILOAD_1 = 27; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ILOAD_2 = 28; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ILOAD_3 = 29; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LLOAD_0 = 30; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LLOAD_1 = 31; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LLOAD_2 = 32; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LLOAD_3 = 33; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FLOAD_0 = 34; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FLOAD_1 = 35; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FLOAD_2 = 36; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FLOAD_3 = 37; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DLOAD_0 = 38; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DLOAD_1 = 39; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DLOAD_2 = 40; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DLOAD_3 = 41; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ALOAD_0 = 42; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ALOAD_1 = 43; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ALOAD_2 = 44; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ALOAD_3 = 45; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IALOAD = 46; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LALOAD = 47; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FALOAD = 48; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DALOAD = 49; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short AALOAD = 50; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short BALOAD = 51; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short CALOAD = 52; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short SALOAD = 53; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ISTORE = 54; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LSTORE = 55; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FSTORE = 56; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DSTORE = 57; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ASTORE = 58; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ISTORE_0 = 59; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ISTORE_1 = 60; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ISTORE_2 = 61; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ISTORE_3 = 62; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short LSTORE_0 = 63; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short LSTORE_1 = 64; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short LSTORE_2 = 65; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short LSTORE_3 = 66; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short FSTORE_0 = 67; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short FSTORE_1 = 68; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short FSTORE_2 = 69; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short FSTORE_3 = 70; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short DSTORE_0 = 71; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short DSTORE_1 = 72; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short DSTORE_2 = 73; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short DSTORE_3 = 74; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ASTORE_0 = 75; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ASTORE_1 = 76; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ASTORE_2 = 77; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ASTORE_3 = 78; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IASTORE = 79; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LASTORE = 80; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FASTORE = 81; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DASTORE = 82; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short AASTORE = 83; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short BASTORE = 84; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short CASTORE = 85; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short SASTORE = 86; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short POP = 87; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short POP2 = 88; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short DUP = 89; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DUP_X1 = 90; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DUP_X2 = 91; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DUP2 = 92; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DUP2_X1 = 93; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DUP2_X2 = 94; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short SWAP = 95; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IADD = 96; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LADD = 97; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FADD = 98; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DADD = 99; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ISUB = 100; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LSUB = 101; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FSUB = 102; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DSUB = 103; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IMUL = 104; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LMUL = 105; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FMUL = 106; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DMUL = 107; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IDIV = 108; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LDIV = 109; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FDIV = 110; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DDIV = 111; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IREM = 112; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LREM = 113; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FREM = 114; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DREM = 115; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short INEG = 116; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LNEG = 117; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FNEG = 118; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DNEG = 119; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ISHL = 120; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LSHL = 121; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ISHR = 122; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LSHR = 123; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IUSHR = 124; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LUSHR = 125; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IAND = 126; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LAND = 127; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short IOR = 128; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short LOR = 129; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IXOR = 130; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LXOR = 131; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IINC = 132; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short I2L = 133; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short I2F = 134; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short I2D = 135; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short L2I = 136; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short L2F = 137; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short L2D = 138; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short F2I = 139; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short F2L = 140; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short F2D = 141; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short D2I = 142; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short D2L = 143; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short D2F = 144; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short I2B = 145; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short INT2BYTE = 145; // Old notation + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short I2C = 146; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short INT2CHAR = 146; // Old notation + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short I2S = 147; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short INT2SHORT = 147; // Old notation + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LCMP = 148; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FCMPL = 149; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FCMPG = 150; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DCMPL = 151; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DCMPG = 152; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IFEQ = 153; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IFNE = 154; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IFLT = 155; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IFGE = 156; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IFGT = 157; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IFLE = 158; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short IF_ICMPEQ = 159; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short IF_ICMPNE = 160; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short IF_ICMPLT = 161; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short IF_ICMPGE = 162; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short IF_ICMPGT = 163; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short IF_ICMPLE = 164; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short IF_ACMPEQ = 165; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short IF_ACMPNE = 166; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short GOTO = 167; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short JSR = 168; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short RET = 169; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short TABLESWITCH = 170; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short LOOKUPSWITCH = 171; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IRETURN = 172; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short LRETURN = 173; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short FRETURN = 174; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short DRETURN = 175; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ARETURN = 176; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short RETURN = 177; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short GETSTATIC = 178; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short PUTSTATIC = 179; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short GETFIELD = 180; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short PUTFIELD = 181; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short INVOKEVIRTUAL = 182; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short INVOKESPECIAL = 183; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short INVOKENONVIRTUAL = 183; // Old name in JDK 1.0 + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short INVOKESTATIC = 184; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short INVOKEINTERFACE = 185; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short INVOKEDYNAMIC = 186; + + /** + * Java VM opcode. + * + * @see Opcode definitions in The + * Java Virtual Machine Specification + */ + public static final short NEW = 187; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short NEWARRAY = 188; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short ANEWARRAY = 189; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short ARRAYLENGTH = 190; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short ATHROW = 191; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short CHECKCAST = 192; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short INSTANCEOF = 193; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short MONITORENTER = 194; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short MONITOREXIT = 195; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short WIDE = 196; + + /** + * Java VM opcode. + * + * @see Opcode + * definitions in The Java Virtual Machine Specification + */ + public static final short MULTIANEWARRAY = 197; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short IFNULL = 198; + + /** + * Java VM opcode. + * + * @see Opcode definitions + * in The Java Virtual Machine Specification + */ + public static final short IFNONNULL = 199; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short GOTO_W = 200; + + /** + * Java VM opcode. + * + * @see Opcode definitions in + * The Java Virtual Machine Specification + */ + public static final short JSR_W = 201; + + /** + * JVM internal opcode. + * + * @see Reserved opcodes in the Java + * Virtual Machine Specification + */ + public static final short BREAKPOINT = 202; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short LDC_QUICK = 203; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short LDC_W_QUICK = 204; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short LDC2_W_QUICK = 205; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short GETFIELD_QUICK = 206; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short PUTFIELD_QUICK = 207; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short GETFIELD2_QUICK = 208; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short PUTFIELD2_QUICK = 209; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short GETSTATIC_QUICK = 210; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short PUTSTATIC_QUICK = 211; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short GETSTATIC2_QUICK = 212; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short PUTSTATIC2_QUICK = 213; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short INVOKEVIRTUAL_QUICK = 214; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short INVOKENONVIRTUAL_QUICK = 215; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short INVOKESUPER_QUICK = 216; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short INVOKESTATIC_QUICK = 217; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short INVOKEINTERFACE_QUICK = 218; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short INVOKEVIRTUALOBJECT_QUICK = 219; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short NEW_QUICK = 221; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short ANEWARRAY_QUICK = 222; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short MULTIANEWARRAY_QUICK = 223; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short CHECKCAST_QUICK = 224; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short INSTANCEOF_QUICK = 225; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short INVOKEVIRTUAL_QUICK_W = 226; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short GETFIELD_QUICK_W = 227; + + /** + * JVM internal opcode. + * + * @see + * Specification of _quick opcodes in the Java Virtual Machine Specification (version 1) + * @see Why the _quick + * opcodes were removed from the second version of the Java Virtual Machine Specification. + */ + public static final short PUTFIELD_QUICK_W = 228; + + /** + * JVM internal opcode. + * + * @see Reserved opcodes in the Java + * Virtual Machine Specification + */ + public static final short IMPDEP1 = 254; + + /** + * JVM internal opcode. + * + * @see Reserved opcodes in the Java + * Virtual Machine Specification + */ + public static final short IMPDEP2 = 255; + + /** + * BCEL virtual instruction for pushing an arbitrary data type onto the stack. Will be converted to the appropriate JVM + * opcode when the class is dumped. + */ + public static final short PUSH = 4711; + + /** + * BCEL virtual instruction for either LOOKUPSWITCH or TABLESWITCH. Will be converted to the appropriate JVM opcode when + * the class is dumped. + */ + public static final short SWITCH = 4712; + + /** Illegal opcode. */ + public static final short UNDEFINED = -1; + + /** Illegal opcode. */ + public static final short UNPREDICTABLE = -2; + + /** Illegal opcode. */ + public static final short RESERVED = -3; + + /** Mnemonic for an illegal opcode. */ + public static final String ILLEGAL_OPCODE = ""; + + /** Mnemonic for an illegal type. */ + public static final String ILLEGAL_TYPE = ""; + + /** + * Boolean data type. + * + * @see Static Constraints in + * the Java Virtual Machine Specification + */ + public static final byte T_BOOLEAN = 4; + + /** + * Char data type. + * + * @see Static Constraints in + * the Java Virtual Machine Specification + */ + public static final byte T_CHAR = 5; + + /** + * Float data type. + * + * @see Static Constraints in + * the Java Virtual Machine Specification + */ + public static final byte T_FLOAT = 6; + + /** + * Double data type. + * + * @see Static Constraints in + * the Java Virtual Machine Specification + */ + public static final byte T_DOUBLE = 7; + + /** + * Byte data type. + * + * @see Static Constraints in + * the Java Virtual Machine Specification + */ + public static final byte T_BYTE = 8; + + /** + * Short data type. + * + * @see Static Constraints in + * the Java Virtual Machine Specification + */ + public static final byte T_SHORT = 9; + + /** + * Int data type. + * + * @see Static Constraints in + * the Java Virtual Machine Specification + */ + public static final byte T_INT = 10; + + /** + * Long data type. + * + * @see Static Constraints in + * the Java Virtual Machine Specification + */ + public static final byte T_LONG = 11; + + /** Void data type (non-standard). */ + public static final byte T_VOID = 12; // Non-standard + + /** Array data type. */ + public static final byte T_ARRAY = 13; + + /** Object data type. */ + public static final byte T_OBJECT = 14; + + /** Reference data type (deprecated). */ + public static final byte T_REFERENCE = 14; // Deprecated + + /** Unknown data type. */ + public static final byte T_UNKNOWN = 15; + + /** Address data type. */ + public static final byte T_ADDRESS = 16; + + /** + * The primitive type names corresponding to the T_XX constants, e.g., TYPE_NAMES[T_INT] = "int" + */ + private static final String[] TYPE_NAMES = {ILLEGAL_TYPE, ILLEGAL_TYPE, ILLEGAL_TYPE, ILLEGAL_TYPE, "boolean", "char", "float", "double", "byte", "short", + "int", "long", "void", "array", "object", "unknown", "address"}; + + /** + * The primitive class names corresponding to the T_XX constants, e.g., CLASS_TYPE_NAMES[T_INT] = "java.lang.Integer" + */ + private static final String[] CLASS_TYPE_NAMES = {ILLEGAL_TYPE, ILLEGAL_TYPE, ILLEGAL_TYPE, ILLEGAL_TYPE, "java.lang.Boolean", "java.lang.Character", + "java.lang.Float", "java.lang.Double", "java.lang.Byte", "java.lang.Short", "java.lang.Integer", "java.lang.Long", "java.lang.Void", ILLEGAL_TYPE, + ILLEGAL_TYPE, ILLEGAL_TYPE, ILLEGAL_TYPE}; + + /** + * The signature characters corresponding to primitive types, e.g., SHORT_TYPE_NAMES[T_INT] = "I" + */ + private static final String[] SHORT_TYPE_NAMES = {ILLEGAL_TYPE, ILLEGAL_TYPE, ILLEGAL_TYPE, ILLEGAL_TYPE, "Z", "C", "F", "D", "B", "S", "I", "J", "V", + ILLEGAL_TYPE, ILLEGAL_TYPE, ILLEGAL_TYPE}; + + /** + * Number of byte code operands for each opcode, i.e., number of bytes after the tag byte itself. Indexed by opcode, so + * NO_OF_OPERANDS[BIPUSH] = the number of operands for a bipush instruction. + */ + static final short[] NO_OF_OPERANDS = {0/* nop */, 0/* aconst_null */, 0/* iconst_m1 */, 0/* iconst_0 */, 0/* iconst_1 */, 0/* iconst_2 */, + 0/* iconst_3 */, 0/* iconst_4 */, 0/* iconst_5 */, 0/* lconst_0 */, 0/* lconst_1 */, 0/* fconst_0 */, 0/* fconst_1 */, 0/* fconst_2 */, 0/* dconst_0 */, + 0/* dconst_1 */, 1/* bipush */, 2/* sipush */, 1/* ldc */, 2/* ldc_w */, 2/* ldc2_w */, 1/* iload */, 1/* lload */, 1/* fload */, 1/* dload */, + 1/* aload */, 0/* iload_0 */, 0/* iload_1 */, 0/* iload_2 */, 0/* iload_3 */, 0/* lload_0 */, 0/* lload_1 */, 0/* lload_2 */, 0/* lload_3 */, + 0/* fload_0 */, 0/* fload_1 */, 0/* fload_2 */, 0/* fload_3 */, 0/* dload_0 */, 0/* dload_1 */, 0/* dload_2 */, 0/* dload_3 */, 0/* aload_0 */, + 0/* aload_1 */, 0/* aload_2 */, 0/* aload_3 */, 0/* iaload */, 0/* laload */, 0/* faload */, 0/* daload */, 0/* aaload */, 0/* baload */, 0/* caload */, + 0/* saload */, 1/* istore */, 1/* lstore */, 1/* fstore */, 1/* dstore */, 1/* astore */, 0/* istore_0 */, 0/* istore_1 */, 0/* istore_2 */, + 0/* istore_3 */, 0/* lstore_0 */, 0/* lstore_1 */, 0/* lstore_2 */, 0/* lstore_3 */, 0/* fstore_0 */, 0/* fstore_1 */, 0/* fstore_2 */, 0/* fstore_3 */, + 0/* dstore_0 */, 0/* dstore_1 */, 0/* dstore_2 */, 0/* dstore_3 */, 0/* astore_0 */, 0/* astore_1 */, 0/* astore_2 */, 0/* astore_3 */, 0/* iastore */, + 0/* lastore */, 0/* fastore */, 0/* dastore */, 0/* aastore */, 0/* bastore */, 0/* castore */, 0/* sastore */, 0/* pop */, 0/* pop2 */, 0/* dup */, + 0/* dup_x1 */, 0/* dup_x2 */, 0/* dup2 */, 0/* dup2_x1 */, 0/* dup2_x2 */, 0/* swap */, 0/* iadd */, 0/* ladd */, 0/* fadd */, 0/* dadd */, 0/* isub */, + 0/* lsub */, 0/* fsub */, 0/* dsub */, 0/* imul */, 0/* lmul */, 0/* fmul */, 0/* dmul */, 0/* idiv */, 0/* ldiv */, 0/* fdiv */, 0/* ddiv */, + 0/* irem */, 0/* lrem */, 0/* frem */, 0/* drem */, 0/* ineg */, 0/* lneg */, 0/* fneg */, 0/* dneg */, 0/* ishl */, 0/* lshl */, 0/* ishr */, + 0/* lshr */, 0/* iushr */, 0/* lushr */, 0/* iand */, 0/* land */, 0/* ior */, 0/* lor */, 0/* ixor */, 0/* lxor */, 2/* iinc */, 0/* i2l */, + 0/* i2f */, 0/* i2d */, 0/* l2i */, 0/* l2f */, 0/* l2d */, 0/* f2i */, 0/* f2l */, 0/* f2d */, 0/* d2i */, 0/* d2l */, 0/* d2f */, 0/* i2b */, + 0/* i2c */, 0/* i2s */, 0/* lcmp */, 0/* fcmpl */, 0/* fcmpg */, 0/* dcmpl */, 0/* dcmpg */, 2/* ifeq */, 2/* ifne */, 2/* iflt */, 2/* ifge */, + 2/* ifgt */, 2/* ifle */, 2/* if_icmpeq */, 2/* if_icmpne */, 2/* if_icmplt */, 2/* if_icmpge */, 2/* if_icmpgt */, 2/* if_icmple */, 2/* if_acmpeq */, + 2/* if_acmpne */, 2/* goto */, 2/* jsr */, 1/* ret */, UNPREDICTABLE/* tableswitch */, UNPREDICTABLE/* lookupswitch */, 0/* ireturn */, 0/* lreturn */, + 0/* freturn */, 0/* dreturn */, 0/* areturn */, 0/* return */, 2/* getstatic */, 2/* putstatic */, 2/* getfield */, 2/* putfield */, + 2/* invokevirtual */, 2/* invokespecial */, 2/* invokestatic */, 4/* invokeinterface */, 4/* invokedynamic */, 2/* new */, 1/* newarray */, + 2/* anewarray */, 0/* arraylength */, 0/* athrow */, 2/* checkcast */, 2/* instanceof */, 0/* monitorenter */, 0/* monitorexit */, + UNPREDICTABLE/* wide */, 3/* multianewarray */, 2/* ifnull */, 2/* ifnonnull */, 4/* goto_w */, 4/* jsr_w */, 0/* breakpoint */, UNDEFINED, UNDEFINED, + UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, + UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, + UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, + UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, RESERVED/* impdep1 */, + RESERVED/* impdep2 */ + }; + + /** + * How the byte code operands are to be interpreted for each opcode. Indexed by opcode. TYPE_OF_OPERANDS[ILOAD] = an + * array of shorts describing the data types for the instruction. + */ + static final short[][] TYPE_OF_OPERANDS = {{}/* nop */, {}/* aconst_null */, {}/* iconst_m1 */, {}/* iconst_0 */, {}/* iconst_1 */, + {}/* iconst_2 */, {}/* iconst_3 */, {}/* iconst_4 */, {}/* iconst_5 */, {}/* lconst_0 */, {}/* lconst_1 */, {}/* fconst_0 */, {}/* fconst_1 */, + {}/* fconst_2 */, {}/* dconst_0 */, {}/* dconst_1 */, {T_BYTE}/* bipush */, {T_SHORT}/* sipush */, {T_BYTE}/* ldc */, {T_SHORT}/* ldc_w */, + {T_SHORT}/* ldc2_w */, {T_BYTE}/* iload */, {T_BYTE}/* lload */, {T_BYTE}/* fload */, {T_BYTE}/* dload */, {T_BYTE}/* aload */, {}/* iload_0 */, + {}/* iload_1 */, {}/* iload_2 */, {}/* iload_3 */, {}/* lload_0 */, {}/* lload_1 */, {}/* lload_2 */, {}/* lload_3 */, {}/* fload_0 */, {}/* fload_1 */, + {}/* fload_2 */, {}/* fload_3 */, {}/* dload_0 */, {}/* dload_1 */, {}/* dload_2 */, {}/* dload_3 */, {}/* aload_0 */, {}/* aload_1 */, {}/* aload_2 */, + {}/* aload_3 */, {}/* iaload */, {}/* laload */, {}/* faload */, {}/* daload */, {}/* aaload */, {}/* baload */, {}/* caload */, {}/* saload */, + {T_BYTE}/* istore */, {T_BYTE}/* lstore */, {T_BYTE}/* fstore */, {T_BYTE}/* dstore */, {T_BYTE}/* astore */, {}/* istore_0 */, {}/* istore_1 */, + {}/* istore_2 */, {}/* istore_3 */, {}/* lstore_0 */, {}/* lstore_1 */, {}/* lstore_2 */, {}/* lstore_3 */, {}/* fstore_0 */, {}/* fstore_1 */, + {}/* fstore_2 */, {}/* fstore_3 */, {}/* dstore_0 */, {}/* dstore_1 */, {}/* dstore_2 */, {}/* dstore_3 */, {}/* astore_0 */, {}/* astore_1 */, + {}/* astore_2 */, {}/* astore_3 */, {}/* iastore */, {}/* lastore */, {}/* fastore */, {}/* dastore */, {}/* aastore */, {}/* bastore */, + {}/* castore */, {}/* sastore */, {}/* pop */, {}/* pop2 */, {}/* dup */, {}/* dup_x1 */, {}/* dup_x2 */, {}/* dup2 */, {}/* dup2_x1 */, + {}/* dup2_x2 */, {}/* swap */, {}/* iadd */, {}/* ladd */, {}/* fadd */, {}/* dadd */, {}/* isub */, {}/* lsub */, {}/* fsub */, {}/* dsub */, + {}/* imul */, {}/* lmul */, {}/* fmul */, {}/* dmul */, {}/* idiv */, {}/* ldiv */, {}/* fdiv */, {}/* ddiv */, {}/* irem */, {}/* lrem */, + {}/* frem */, {}/* drem */, {}/* ineg */, {}/* lneg */, {}/* fneg */, {}/* dneg */, {}/* ishl */, {}/* lshl */, {}/* ishr */, {}/* lshr */, + {}/* iushr */, {}/* lushr */, {}/* iand */, {}/* land */, {}/* ior */, {}/* lor */, {}/* ixor */, {}/* lxor */, {T_BYTE, T_BYTE}/* iinc */, {}/* i2l */, + {}/* i2f */, {}/* i2d */, {}/* l2i */, {}/* l2f */, {}/* l2d */, {}/* f2i */, {}/* f2l */, {}/* f2d */, {}/* d2i */, {}/* d2l */, {}/* d2f */, + {}/* i2b */, {}/* i2c */, {}/* i2s */, {}/* lcmp */, {}/* fcmpl */, {}/* fcmpg */, {}/* dcmpl */, {}/* dcmpg */, {T_SHORT}/* ifeq */, + {T_SHORT}/* ifne */, {T_SHORT}/* iflt */, {T_SHORT}/* ifge */, {T_SHORT}/* ifgt */, {T_SHORT}/* ifle */, {T_SHORT}/* if_icmpeq */, + {T_SHORT}/* if_icmpne */, {T_SHORT}/* if_icmplt */, {T_SHORT}/* if_icmpge */, {T_SHORT}/* if_icmpgt */, {T_SHORT}/* if_icmple */, + {T_SHORT}/* if_acmpeq */, {T_SHORT}/* if_acmpne */, {T_SHORT}/* goto */, {T_SHORT}/* jsr */, {T_BYTE}/* ret */, {}/* tableswitch */, + {}/* lookupswitch */, {}/* ireturn */, {}/* lreturn */, {}/* freturn */, {}/* dreturn */, {}/* areturn */, {}/* return */, {T_SHORT}/* getstatic */, + {T_SHORT}/* putstatic */, {T_SHORT}/* getfield */, {T_SHORT}/* putfield */, {T_SHORT}/* invokevirtual */, {T_SHORT}/* invokespecial */, + {T_SHORT}/* invokestatic */, {T_SHORT, T_BYTE, T_BYTE}/* invokeinterface */, {T_SHORT, T_BYTE, T_BYTE}/* invokedynamic */, {T_SHORT}/* new */, + {T_BYTE}/* newarray */, {T_SHORT}/* anewarray */, {}/* arraylength */, {}/* athrow */, {T_SHORT}/* checkcast */, {T_SHORT}/* instanceof */, + {}/* monitorenter */, {}/* monitorexit */, {T_BYTE}/* wide */, {T_SHORT, T_BYTE}/* multianewarray */, {T_SHORT}/* ifnull */, {T_SHORT}/* ifnonnull */, + {T_INT}/* goto_w */, {T_INT}/* jsr_w */, {}/* breakpoint */, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, + {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}/* impdep1 */, {}/* impdep2 */ + }; + + /** + * Names of opcodes. Indexed by opcode. OPCODE_NAMES[ALOAD] = "aload". + */ + static final String[] OPCODE_NAMES = {"nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2", "iconst_3", "iconst_4", "iconst_5", + "lconst_0", "lconst_1", "fconst_0", "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush", "ldc", "ldc_w", "ldc2_w", "iload", "lload", + "fload", "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3", "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1", "fload_2", + "fload_3", "dload_0", "dload_1", "dload_2", "dload_3", "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload", "faload", "daload", "aaload", + "baload", "caload", "saload", "istore", "lstore", "fstore", "dstore", "astore", "istore_0", "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", + "lstore_2", "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0", "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", + "astore_2", "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore", "bastore", "castore", "sastore", "pop", "pop2", "dup", "dup_x1", + "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap", "iadd", "ladd", "fadd", "dadd", "isub", "lsub", "fsub", "dsub", "imul", "lmul", "fmul", "dmul", "idiv", + "ldiv", "fdiv", "ddiv", "irem", "lrem", "frem", "drem", "ineg", "lneg", "fneg", "dneg", "ishl", "lshl", "ishr", "lshr", "iushr", "lushr", "iand", + "land", "ior", "lor", "ixor", "lxor", "iinc", "i2l", "i2f", "i2d", "l2i", "l2f", "l2d", "f2i", "f2l", "f2d", "d2i", "d2l", "d2f", "i2b", "i2c", "i2s", + "lcmp", "fcmpl", "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt", "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt", "if_icmpge", + "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto", "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn", "freturn", "dreturn", + "areturn", "return", "getstatic", "putstatic", "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface", + "invokedynamic", "new", "newarray", "anewarray", "arraylength", "athrow", "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", + "multianewarray", "ifnull", "ifnonnull", "goto_w", "jsr_w", "breakpoint", ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, + ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, + ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, + ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, + ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, + ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, + ILLEGAL_OPCODE, ILLEGAL_OPCODE, "impdep1", "impdep2"}; + + /** + * @since 6.0 + */ + public static final int OPCODE_NAMES_LENGTH = OPCODE_NAMES.length; + + /** + * Number of words consumed on operand stack by instructions. Indexed by opcode. CONSUME_STACK[FALOAD] = number of words + * consumed from the stack by a faload instruction. + */ + static final int[] CONSUME_STACK = {0/* nop */, 0/* aconst_null */, 0/* iconst_m1 */, 0/* iconst_0 */, 0/* iconst_1 */, 0/* iconst_2 */, + 0/* iconst_3 */, 0/* iconst_4 */, 0/* iconst_5 */, 0/* lconst_0 */, 0/* lconst_1 */, 0/* fconst_0 */, 0/* fconst_1 */, 0/* fconst_2 */, 0/* dconst_0 */, + 0/* dconst_1 */, 0/* bipush */, 0/* sipush */, 0/* ldc */, 0/* ldc_w */, 0/* ldc2_w */, 0/* iload */, 0/* lload */, 0/* fload */, 0/* dload */, + 0/* aload */, 0/* iload_0 */, 0/* iload_1 */, 0/* iload_2 */, 0/* iload_3 */, 0/* lload_0 */, 0/* lload_1 */, 0/* lload_2 */, 0/* lload_3 */, + 0/* fload_0 */, 0/* fload_1 */, 0/* fload_2 */, 0/* fload_3 */, 0/* dload_0 */, 0/* dload_1 */, 0/* dload_2 */, 0/* dload_3 */, 0/* aload_0 */, + 0/* aload_1 */, 0/* aload_2 */, 0/* aload_3 */, 2/* iaload */, 2/* laload */, 2/* faload */, 2/* daload */, 2/* aaload */, 2/* baload */, 2/* caload */, + 2/* saload */, 1/* istore */, 2/* lstore */, 1/* fstore */, 2/* dstore */, 1/* astore */, 1/* istore_0 */, 1/* istore_1 */, 1/* istore_2 */, + 1/* istore_3 */, 2/* lstore_0 */, 2/* lstore_1 */, 2/* lstore_2 */, 2/* lstore_3 */, 1/* fstore_0 */, 1/* fstore_1 */, 1/* fstore_2 */, 1/* fstore_3 */, + 2/* dstore_0 */, 2/* dstore_1 */, 2/* dstore_2 */, 2/* dstore_3 */, 1/* astore_0 */, 1/* astore_1 */, 1/* astore_2 */, 1/* astore_3 */, 3/* iastore */, + 4/* lastore */, 3/* fastore */, 4/* dastore */, 3/* aastore */, 3/* bastore */, 3/* castore */, 3/* sastore */, 1/* pop */, 2/* pop2 */, 1/* dup */, + 2/* dup_x1 */, 3/* dup_x2 */, 2/* dup2 */, 3/* dup2_x1 */, 4/* dup2_x2 */, 2/* swap */, 2/* iadd */, 4/* ladd */, 2/* fadd */, 4/* dadd */, 2/* isub */, + 4/* lsub */, 2/* fsub */, 4/* dsub */, 2/* imul */, 4/* lmul */, 2/* fmul */, 4/* dmul */, 2/* idiv */, 4/* ldiv */, 2/* fdiv */, 4/* ddiv */, + 2/* irem */, 4/* lrem */, 2/* frem */, 4/* drem */, 1/* ineg */, 2/* lneg */, 1/* fneg */, 2/* dneg */, 2/* ishl */, 3/* lshl */, 2/* ishr */, + 3/* lshr */, 2/* iushr */, 3/* lushr */, 2/* iand */, 4/* land */, 2/* ior */, 4/* lor */, 2/* ixor */, 4/* lxor */, 0/* iinc */, 1/* i2l */, + 1/* i2f */, 1/* i2d */, 2/* l2i */, 2/* l2f */, 2/* l2d */, 1/* f2i */, 1/* f2l */, 1/* f2d */, 2/* d2i */, 2/* d2l */, 2/* d2f */, 1/* i2b */, + 1/* i2c */, 1/* i2s */, 4/* lcmp */, 2/* fcmpl */, 2/* fcmpg */, 4/* dcmpl */, 4/* dcmpg */, 1/* ifeq */, 1/* ifne */, 1/* iflt */, 1/* ifge */, + 1/* ifgt */, 1/* ifle */, 2/* if_icmpeq */, 2/* if_icmpne */, 2/* if_icmplt */, 2 /* if_icmpge */, 2/* if_icmpgt */, 2/* if_icmple */, 2/* if_acmpeq */, + 2/* if_acmpne */, 0/* goto */, 0/* jsr */, 0/* ret */, 1/* tableswitch */, 1/* lookupswitch */, 1/* ireturn */, 2/* lreturn */, 1/* freturn */, + 2/* dreturn */, 1/* areturn */, 0/* return */, 0/* getstatic */, UNPREDICTABLE/* putstatic */, 1/* getfield */, UNPREDICTABLE/* putfield */, + UNPREDICTABLE/* invokevirtual */, UNPREDICTABLE/* invokespecial */, UNPREDICTABLE/* invokestatic */, UNPREDICTABLE/* invokeinterface */, + UNPREDICTABLE/* invokedynamic */, 0/* new */, 1/* newarray */, 1/* anewarray */, 1/* arraylength */, 1/* athrow */, 1/* checkcast */, 1/* instanceof */, + 1/* monitorenter */, 1/* monitorexit */, 0/* wide */, UNPREDICTABLE/* multianewarray */, 1/* ifnull */, 1/* ifnonnull */, 0/* goto_w */, 0/* jsr_w */, + 0/* breakpoint */, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, + UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, + UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, + UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, + UNPREDICTABLE/* impdep1 */, UNPREDICTABLE/* impdep2 */ + }; + + /** + * Number of words produced onto operand stack by instructions. Indexed by opcode. CONSUME_STACK[DALOAD] = number of + * words consumed from the stack by a daload instruction. + */ + static final int[] PRODUCE_STACK = {0/* nop */, 1/* aconst_null */, 1/* iconst_m1 */, 1/* iconst_0 */, 1/* iconst_1 */, 1/* iconst_2 */, + 1/* iconst_3 */, 1/* iconst_4 */, 1/* iconst_5 */, 2/* lconst_0 */, 2/* lconst_1 */, 1/* fconst_0 */, 1/* fconst_1 */, 1/* fconst_2 */, 2/* dconst_0 */, + 2/* dconst_1 */, 1/* bipush */, 1/* sipush */, 1/* ldc */, 1/* ldc_w */, 2/* ldc2_w */, 1/* iload */, 2/* lload */, 1/* fload */, 2/* dload */, + 1/* aload */, 1/* iload_0 */, 1/* iload_1 */, 1/* iload_2 */, 1/* iload_3 */, 2/* lload_0 */, 2/* lload_1 */, 2/* lload_2 */, 2/* lload_3 */, + 1/* fload_0 */, 1/* fload_1 */, 1/* fload_2 */, 1/* fload_3 */, 2/* dload_0 */, 2/* dload_1 */, 2/* dload_2 */, 2/* dload_3 */, 1/* aload_0 */, + 1/* aload_1 */, 1/* aload_2 */, 1/* aload_3 */, 1/* iaload */, 2/* laload */, 1/* faload */, 2/* daload */, 1/* aaload */, 1/* baload */, 1/* caload */, + 1/* saload */, 0/* istore */, 0/* lstore */, 0/* fstore */, 0/* dstore */, 0/* astore */, 0/* istore_0 */, 0/* istore_1 */, 0/* istore_2 */, + 0/* istore_3 */, 0/* lstore_0 */, 0/* lstore_1 */, 0/* lstore_2 */, 0/* lstore_3 */, 0/* fstore_0 */, 0/* fstore_1 */, 0/* fstore_2 */, 0/* fstore_3 */, + 0/* dstore_0 */, 0/* dstore_1 */, 0/* dstore_2 */, 0/* dstore_3 */, 0/* astore_0 */, 0/* astore_1 */, 0/* astore_2 */, 0/* astore_3 */, 0/* iastore */, + 0/* lastore */, 0/* fastore */, 0/* dastore */, 0/* aastore */, 0/* bastore */, 0/* castore */, 0/* sastore */, 0/* pop */, 0/* pop2 */, 2/* dup */, + 3/* dup_x1 */, 4/* dup_x2 */, 4/* dup2 */, 5/* dup2_x1 */, 6/* dup2_x2 */, 2/* swap */, 1/* iadd */, 2/* ladd */, 1/* fadd */, 2/* dadd */, 1/* isub */, + 2/* lsub */, 1/* fsub */, 2/* dsub */, 1/* imul */, 2/* lmul */, 1/* fmul */, 2/* dmul */, 1/* idiv */, 2/* ldiv */, 1/* fdiv */, 2/* ddiv */, + 1/* irem */, 2/* lrem */, 1/* frem */, 2/* drem */, 1/* ineg */, 2/* lneg */, 1/* fneg */, 2/* dneg */, 1/* ishl */, 2/* lshl */, 1/* ishr */, + 2/* lshr */, 1/* iushr */, 2/* lushr */, 1/* iand */, 2/* land */, 1/* ior */, 2/* lor */, 1/* ixor */, 2/* lxor */, 0/* iinc */, 2/* i2l */, + 1/* i2f */, 2/* i2d */, 1/* l2i */, 1/* l2f */, 2/* l2d */, 1/* f2i */, 2/* f2l */, 2/* f2d */, 1/* d2i */, 2/* d2l */, 1/* d2f */, 1/* i2b */, + 1/* i2c */, 1/* i2s */, 1/* lcmp */, 1/* fcmpl */, 1/* fcmpg */, 1/* dcmpl */, 1/* dcmpg */, 0/* ifeq */, 0/* ifne */, 0/* iflt */, 0/* ifge */, + 0/* ifgt */, 0/* ifle */, 0/* if_icmpeq */, 0/* if_icmpne */, 0/* if_icmplt */, 0/* if_icmpge */, 0/* if_icmpgt */, 0/* if_icmple */, 0/* if_acmpeq */, + 0/* if_acmpne */, 0/* goto */, 1/* jsr */, 0/* ret */, 0/* tableswitch */, 0/* lookupswitch */, 0/* ireturn */, 0/* lreturn */, 0/* freturn */, + 0/* dreturn */, 0/* areturn */, 0/* return */, UNPREDICTABLE/* getstatic */, 0/* putstatic */, UNPREDICTABLE/* getfield */, 0/* putfield */, + UNPREDICTABLE/* invokevirtual */, UNPREDICTABLE/* invokespecial */, UNPREDICTABLE/* invokestatic */, UNPREDICTABLE/* invokeinterface */, + UNPREDICTABLE/* invokedynamic */, 1/* new */, 1/* newarray */, 1/* anewarray */, 1/* arraylength */, 1/* athrow */, 1/* checkcast */, 1/* instanceof */, + 0/* monitorenter */, 0/* monitorexit */, 0/* wide */, 1/* multianewarray */, 0/* ifnull */, 0/* ifnonnull */, 0/* goto_w */, 1/* jsr_w */, + 0/* breakpoint */, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, + UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, + UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, + UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, + UNPREDICTABLE/* impdep1 */, UNPREDICTABLE/* impdep2 */ + }; + + /** + * Attributes and their corresponding names. + */ + public static final byte ATTR_UNKNOWN = -1; + + public static final byte ATTR_SOURCE_FILE = 0; + + public static final byte ATTR_CONSTANT_VALUE = 1; + + public static final byte ATTR_CODE = 2; + + public static final byte ATTR_EXCEPTIONS = 3; + + public static final byte ATTR_LINE_NUMBER_TABLE = 4; + + public static final byte ATTR_LOCAL_VARIABLE_TABLE = 5; + + public static final byte ATTR_INNER_CLASSES = 6; + + public static final byte ATTR_SYNTHETIC = 7; + + public static final byte ATTR_DEPRECATED = 8; + + public static final byte ATTR_PMG = 9; + + public static final byte ATTR_SIGNATURE = 10; + + public static final byte ATTR_STACK_MAP = 11; + public static final byte ATTR_RUNTIME_VISIBLE_ANNOTATIONS = 12; + public static final byte ATTR_RUNTIME_INVISIBLE_ANNOTATIONS = 13; + public static final byte ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = 14; + public static final byte ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = 15; + public static final byte ATTR_ANNOTATION_DEFAULT = 16; + public static final byte ATTR_LOCAL_VARIABLE_TYPE_TABLE = 17; + public static final byte ATTR_ENCLOSING_METHOD = 18; + public static final byte ATTR_STACK_MAP_TABLE = 19; + public static final byte ATTR_BOOTSTRAP_METHODS = 20; + public static final byte ATTR_METHOD_PARAMETERS = 21; + public static final byte ATTR_MODULE = 22; + public static final byte ATTR_MODULE_PACKAGES = 23; + public static final byte ATTR_MODULE_MAIN_CLASS = 24; + public static final byte ATTR_NEST_HOST = 25; + public static final byte ATTR_NEST_MEMBERS = 26; + public static final short KNOWN_ATTRIBUTES = 27; // count of attributes + private static final String[] ATTRIBUTE_NAMES = {"SourceFile", "ConstantValue", "Code", "Exceptions", "LineNumberTable", "LocalVariableTable", + "InnerClasses", "Synthetic", "Deprecated", "PMGClass", "Signature", "StackMap", "RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations", + "RuntimeVisibleParameterAnnotations", "RuntimeInvisibleParameterAnnotations", "AnnotationDefault", "LocalVariableTypeTable", "EnclosingMethod", + "StackMapTable", "BootstrapMethods", "MethodParameters", "Module", "ModulePackages", "ModuleMainClass", "NestHost", "NestMembers"}; + /** + * Constants used in the StackMap attribute. + */ + public static final byte ITEM_Bogus = 0; + public static final byte ITEM_Integer = 1; + public static final byte ITEM_Float = 2; + public static final byte ITEM_Double = 3; + public static final byte ITEM_Long = 4; + public static final byte ITEM_Null = 5; + public static final byte ITEM_InitObject = 6; + public static final byte ITEM_Object = 7; + public static final byte ITEM_NewObject = 8; + private static final String[] ITEM_NAMES = {"Bogus", "Integer", "Float", "Double", "Long", "Null", "InitObject", "Object", "NewObject"}; + + /** + * Constants used to identify StackMapEntry types. + * + * For those types which can specify a range, the constant names the lowest value. + */ + public static final int SAME_FRAME = 0; + + public static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; + + public static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; + + public static final int CHOP_FRAME = 248; + public static final int SAME_FRAME_EXTENDED = 251; + public static final int APPEND_FRAME = 252; + public static final int FULL_FRAME = 255; + /** + * Constants that define the maximum value of those constants which store ranges. + */ + + public static final int SAME_FRAME_MAX = 63; + public static final int SAME_LOCALS_1_STACK_ITEM_FRAME_MAX = 127; + public static final int CHOP_FRAME_MAX = 250; + public static final int APPEND_FRAME_MAX = 254; + public static final byte REF_getField = 1; + + public static final byte REF_getStatic = 2; + + public static final byte REF_putField = 3; + + public static final byte REF_putStatic = 4; + public static final byte REF_invokeVirtual = 5; + public static final byte REF_invokeStatic = 6; + public static final byte REF_invokeSpecial = 7; + public static final byte REF_newInvokeSpecial = 8; + public static final byte REF_invokeInterface = 9; + /** + * The names of the reference_kinds of a CONSTANT_MethodHandle_info. + */ + private static final String[] METHODHANDLE_NAMES = {"", "getField", "getStatic", "putField", "putStatic", "invokeVirtual", "invokeStatic", "invokeSpecial", + "newInvokeSpecial", "invokeInterface"}; + + /** + * @param index + * @return the ACCESS_NAMES entry at the given index + * @since 6.0 + */ + public static String getAccessName(final int index) { + return ACCESS_NAMES[index]; + } + + /** + * + * @param index + * @return the attribute name + * @since 6.0 + */ + public static String getAttributeName(final int index) { + return ATTRIBUTE_NAMES[index]; + } + + /** + * The primitive class names corresponding to the T_XX constants, e.g., CLASS_TYPE_NAMES[T_INT] = "java.lang.Integer" + * + * @param index + * @return the class name + * @since 6.0 + */ + public static String getClassTypeName(final int index) { + return CLASS_TYPE_NAMES[index]; + } + + /** + * + * @param index + * @return the CONSTANT_NAMES entry at the given index + * @since 6.0 + */ + public static String getConstantName(final int index) { + return CONSTANT_NAMES[index]; + } + + // Constants defining the behavior of the Method Handles (JVMS �5.4.3.5) + + /** + * + * @param index + * @return Number of words consumed on operand stack + * @since 6.0 + */ + public static int getConsumeStack(final int index) { + return CONSUME_STACK[index]; + } + + /** + * @since 6.0 + */ + public static Iterable getInterfacesImplementedByArrays() { + return Collections.unmodifiableList(Arrays.asList(INTERFACES_IMPLEMENTED_BY_ARRAYS)); + } + + /** + * + * @param index + * @return the item name + * @since 6.0 + */ + public static String getItemName(final int index) { + return ITEM_NAMES[index]; + } + + /** + * + * @param index + * @return the method handle name + * @since 6.0 + */ + public static String getMethodHandleName(final int index) { + return METHODHANDLE_NAMES[index]; + } + + /** + * + * @param index + * @return Number of byte code operands + * @since 6.0 + */ + public static short getNoOfOperands(final int index) { + return NO_OF_OPERANDS[index]; + } + + /** + * @since 6.0 + */ + public static String getOpcodeName(final int index) { + return OPCODE_NAMES[index]; + } + + /** + * @since 6.0 + */ + public static short getOperandType(final int opcode, final int index) { + return TYPE_OF_OPERANDS[opcode][index]; + } + + /** + * @since 6.0 + */ + public static long getOperandTypeCount(final int opcode) { + return TYPE_OF_OPERANDS[opcode].length; + } + + /** + * + * @param index + * @return Number of words produced onto operand stack + * @since 6.0 + */ + public static int getProduceStack(final int index) { + return PRODUCE_STACK[index]; + } + + /** + * + * @param index + * @return the short type name + * @since 6.0 + */ + public static String getShortTypeName(final int index) { + return SHORT_TYPE_NAMES[index]; + } + + /** + * The primitive type names corresponding to the T_XX constants, e.g., TYPE_NAMES[T_INT] = "int" + * + * @param index + * @return the type name + * @since 6.0 + */ + public static String getTypeName(final int index) { + return TYPE_NAMES[index]; + } + + private Const() { + } // not instantiable + +} diff --git a/src/main/java/haidnor/jvm/bcel/ExceptionConst.java b/src/main/java/haidnor/jvm/bcel/ExceptionConst.java new file mode 100644 index 0000000..e9a96df --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/ExceptionConst.java @@ -0,0 +1,128 @@ +/* + * 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 haidnor.jvm.bcel; + +import org.apache.commons.lang3.ArrayUtils; + +/** + * Exception constants. + * + * @since 6.0 (intended to replace the InstructionConstant interface) + */ +public final class ExceptionConst { + + /** + * Enum corresponding to the various Exception Class arrays, used by + * {@link ExceptionConst#createExceptions(EXCS, Class...)} + */ + public enum EXCS { + EXCS_CLASS_AND_INTERFACE_RESOLUTION, EXCS_FIELD_AND_METHOD_RESOLUTION, EXCS_INTERFACE_METHOD_RESOLUTION, EXCS_STRING_RESOLUTION, EXCS_ARRAY_EXCEPTION, + } + + /** + * The mother of all exceptions + */ + public static final Class THROWABLE = Throwable.class; + + /** + * Super class of any run-time exception + */ + public static final Class RUNTIME_EXCEPTION = RuntimeException.class; + + /** + * Super class of any linking exception (aka Linkage Error) + */ + public static final Class LINKING_EXCEPTION = LinkageError.class; + /** + * Linking Exceptions + */ + public static final Class CLASS_CIRCULARITY_ERROR = ClassCircularityError.class; + public static final Class CLASS_FORMAT_ERROR = ClassFormatError.class; + public static final Class EXCEPTION_IN_INITIALIZER_ERROR = ExceptionInInitializerError.class; + public static final Class INCOMPATIBLE_CLASS_CHANGE_ERROR = IncompatibleClassChangeError.class; + public static final Class ABSTRACT_METHOD_ERROR = AbstractMethodError.class; + public static final Class ILLEGAL_ACCESS_ERROR = IllegalAccessError.class; + public static final Class INSTANTIATION_ERROR = InstantiationError.class; + public static final Class NO_SUCH_FIELD_ERROR = NoSuchFieldError.class; + public static final Class NO_SUCH_METHOD_ERROR = NoSuchMethodError.class; + public static final Class NO_CLASS_DEF_FOUND_ERROR = NoClassDefFoundError.class; + public static final Class UNSATISFIED_LINK_ERROR = UnsatisfiedLinkError.class; + + public static final Class VERIFY_ERROR = VerifyError.class; + /* UnsupportedClassVersionError is new in JDK 1.2 */ +// public static final Class UnsupportedClassVersionError = UnsupportedClassVersionError.class; + /** + * Run-Time Exceptions + */ + public static final Class NULL_POINTER_EXCEPTION = NullPointerException.class; + public static final Class ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION = ArrayIndexOutOfBoundsException.class; + public static final Class ARITHMETIC_EXCEPTION = ArithmeticException.class; + public static final Class NEGATIVE_ARRAY_SIZE_EXCEPTION = NegativeArraySizeException.class; + public static final Class CLASS_CAST_EXCEPTION = ClassCastException.class; + + public static final Class ILLEGAL_MONITOR_STATE = IllegalMonitorStateException.class; + /** + * Pre-defined exception arrays according to chapters 5.1-5.4 of the Java Virtual Machine Specification + */ + private static final Class[] EXCS_CLASS_AND_INTERFACE_RESOLUTION = {NO_CLASS_DEF_FOUND_ERROR, CLASS_FORMAT_ERROR, VERIFY_ERROR, ABSTRACT_METHOD_ERROR, + EXCEPTION_IN_INITIALIZER_ERROR, ILLEGAL_ACCESS_ERROR}; // Chapter 5.1 + + private static final Class[] EXCS_FIELD_AND_METHOD_RESOLUTION = {NO_SUCH_FIELD_ERROR, ILLEGAL_ACCESS_ERROR, NO_SUCH_METHOD_ERROR}; // Chapter 5.2 + + /** + * Empty array. + */ + private static final Class[] EXCS_INTERFACE_METHOD_RESOLUTION = new Class[0]; // Chapter 5.3 (as below) + + /** + * Empty array. + */ + private static final Class[] EXCS_STRING_RESOLUTION = new Class[0]; + + // Chapter 5.4 (no errors but the ones that _always_ could happen! How stupid.) + private static final Class[] EXCS_ARRAY_EXCEPTION = {NULL_POINTER_EXCEPTION, ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION}; + + /** + * Creates a copy of the specified Exception Class array combined with any additional Exception classes. + * + * @param type the basic array type + * @param extraClasses additional classes, if any + * @return the merged array + */ + public static Class[] createExceptions(final EXCS type, final Class... extraClasses) { + switch (type) { + case EXCS_CLASS_AND_INTERFACE_RESOLUTION: + return mergeExceptions(EXCS_CLASS_AND_INTERFACE_RESOLUTION, extraClasses); + case EXCS_ARRAY_EXCEPTION: + return mergeExceptions(EXCS_ARRAY_EXCEPTION, extraClasses); + case EXCS_FIELD_AND_METHOD_RESOLUTION: + return mergeExceptions(EXCS_FIELD_AND_METHOD_RESOLUTION, extraClasses); + case EXCS_INTERFACE_METHOD_RESOLUTION: + return mergeExceptions(EXCS_INTERFACE_METHOD_RESOLUTION, extraClasses); + case EXCS_STRING_RESOLUTION: + return mergeExceptions(EXCS_STRING_RESOLUTION, extraClasses); + default: + throw new AssertionError("Cannot happen; unexpected enum value: " + type); + } + } + + // helper method to merge exception class arrays + private static Class[] mergeExceptions(final Class[] input, final Class... extraClasses) { + return ArrayUtils.addAll(input, extraClasses); + } + +} diff --git a/src/main/java/haidnor/jvm/bcel/Repository.java b/src/main/java/haidnor/jvm/bcel/Repository.java new file mode 100644 index 0000000..b05600d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/Repository.java @@ -0,0 +1,215 @@ +/* + * 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 haidnor.jvm.bcel; + +import haidnor.jvm.bcel.classfile.JavaClass; +import haidnor.jvm.bcel.util.ClassPath; +import haidnor.jvm.bcel.util.SyntheticRepository; + +import java.io.IOException; + +/** + * The repository maintains informations about class interdependencies, e.g., whether a class is a sub-class of another. + * Delegates actual class loading to SyntheticRepository with current class path by default. + * + * @see haidnor.jvm.bcel.util.Repository + * @see SyntheticRepository + */ +public abstract class Repository { + + private static haidnor.jvm.bcel.util.Repository repository = SyntheticRepository.getInstance(); + + /** + * Adds clazz to repository if there isn't an equally named class already in there. + * + * @return old entry in repository + */ + public static JavaClass addClass(final JavaClass clazz) { + final JavaClass old = repository.findClass(clazz.getClassName()); + repository.storeClass(clazz); + return old; + } + + /** + * Clears the repository. + */ + public static void clearCache() { + repository.clear(); + } + + /** + * @return all interfaces implemented by class and its super classes and the interfaces that those interfaces extend, + * and so on. (Some people call this a transitive hull). + * @throws ClassNotFoundException if any of the class's superclasses or superinterfaces can't be found + */ + public static JavaClass[] getInterfaces(final JavaClass clazz) throws ClassNotFoundException { + return clazz.getAllInterfaces(); + } + + /** + * @return all interfaces implemented by class and its super classes and the interfaces that extend those interfaces, + * and so on + * @throws ClassNotFoundException if the named class can't be found, or if any of its superclasses or superinterfaces + * can't be found + */ + public static JavaClass[] getInterfaces(final String className) throws ClassNotFoundException { + return getInterfaces(lookupClass(className)); + } + + /** + * @return currently used repository instance + */ + public static haidnor.jvm.bcel.util.Repository getRepository() { + return repository; + } + + /** + * @return list of super classes of clazz in ascending order, i.e., Object is always the last element + * @throws ClassNotFoundException if any of the superclasses can't be found + */ + public static JavaClass[] getSuperClasses(final JavaClass clazz) throws ClassNotFoundException { + return clazz.getSuperClasses(); + } + + /** + * @return list of super classes of clazz in ascending order, i.e., Object is always the last element. + * @throws ClassNotFoundException if the named class or any of its superclasses can't be found + */ + public static JavaClass[] getSuperClasses(final String className) throws ClassNotFoundException { + return getSuperClasses(lookupClass(className)); + } + + /** + * @return true, if clazz is an implementation of interface inter + * @throws ClassNotFoundException if any superclasses or superinterfaces of clazz can't be found + */ + public static boolean implementationOf(final JavaClass clazz, final JavaClass inter) throws ClassNotFoundException { + return clazz.implementationOf(inter); + } + + /** + * @return true, if clazz is an implementation of interface inter + * @throws ClassNotFoundException if inter or any superclasses or superinterfaces of clazz can't be found + */ + public static boolean implementationOf(final JavaClass clazz, final String inter) throws ClassNotFoundException { + return implementationOf(clazz, lookupClass(inter)); + } + + /** + * @return true, if clazz is an implementation of interface inter + * @throws ClassNotFoundException if clazz or any superclasses or superinterfaces of clazz can't be found + */ + public static boolean implementationOf(final String clazz, final JavaClass inter) throws ClassNotFoundException { + return implementationOf(lookupClass(clazz), inter); + } + + /** + * @return true, if clazz is an implementation of interface inter + * @throws ClassNotFoundException if clazz, inter, or any superclasses or superinterfaces of clazz can't be found + */ + public static boolean implementationOf(final String clazz, final String inter) throws ClassNotFoundException { + return implementationOf(lookupClass(clazz), lookupClass(inter)); + } + + /** + * Equivalent to runtime "instanceof" operator. + * + * @return true, if clazz is an instance of superclass + * @throws ClassNotFoundException if any superclasses or superinterfaces of clazz can't be found + */ + public static boolean instanceOf(final JavaClass clazz, final JavaClass superclass) throws ClassNotFoundException { + return clazz.instanceOf(superclass); + } + + /** + * @return true, if clazz is an instance of superclass + * @throws ClassNotFoundException if superclass can't be found + */ + public static boolean instanceOf(final JavaClass clazz, final String superclass) throws ClassNotFoundException { + return instanceOf(clazz, lookupClass(superclass)); + } + + /** + * @return true, if clazz is an instance of superclass + * @throws ClassNotFoundException if clazz can't be found + */ + public static boolean instanceOf(final String clazz, final JavaClass superclass) throws ClassNotFoundException { + return instanceOf(lookupClass(clazz), superclass); + } + + /** + * @return true, if clazz is an instance of superclass + * @throws ClassNotFoundException if either clazz or superclass can't be found + */ + public static boolean instanceOf(final String clazz, final String superclass) throws ClassNotFoundException { + return instanceOf(lookupClass(clazz), lookupClass(superclass)); + } + + /** + * Tries to find class source using the internal repository instance. + * + * @see Class + * @return JavaClass object for given runtime class + * @throws ClassNotFoundException if the class could not be found or parsed correctly + */ + public static JavaClass lookupClass(final Class clazz) throws ClassNotFoundException { + return repository.loadClass(clazz); + } + + /** + * Lookups class somewhere found on your CLASSPATH, or whereever the repository instance looks for it. + * + * @return class object for given fully qualified class name + * @throws ClassNotFoundException if the class could not be found or parsed correctly + */ + public static JavaClass lookupClass(final String className) throws ClassNotFoundException { + return repository.loadClass(className); + } + + /** + * @return class file object for given Java class by looking on the system class path; returns null if the class file + * can't be found + */ + public static ClassPath.ClassFile lookupClassFile(final String className) { + try (ClassPath path = repository.getClassPath()) { + return path == null ? null : path.getClassFile(className); + } catch (final IOException e) { + return null; + } + } + + /** + * Removes given class from repository. + */ + public static void removeClass(final JavaClass clazz) { + repository.removeClass(clazz); + } + + /** + * Removes class with given (fully qualified) name from repository. + */ + public static void removeClass(final String clazz) { + repository.removeClass(repository.findClass(clazz)); + } + + /** + * Sets repository instance to be used for class loading + */ + public static void setRepository(final haidnor.jvm.bcel.util.Repository rep) { + repository = rep; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/AccessFlags.java b/src/main/java/haidnor/jvm/bcel/classfile/AccessFlags.java new file mode 100644 index 0000000..a631ad5 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/AccessFlags.java @@ -0,0 +1,211 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +/** + * Super class for all objects that have modifiers like private, final, ... I.e. classes, fields, and methods. + */ +public abstract class AccessFlags { + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected int access_flags; // TODO not used externally at present + + public AccessFlags() { + } + + /** + * @param a initial access flags + */ + public AccessFlags(final int a) { + access_flags = a; + } + + /** + * @return Access flags of the object aka. "modifiers". + */ + public final int getAccessFlags() { + return access_flags; + } + + /** + * @return Access flags of the object aka. "modifiers". + */ + public final int getModifiers() { + return access_flags; + } + + public final boolean isAbstract() { + return (access_flags & Const.ACC_ABSTRACT) != 0; + } + + public final void isAbstract(final boolean flag) { + setFlag(Const.ACC_ABSTRACT, flag); + } + + public final boolean isAnnotation() { + return (access_flags & Const.ACC_ANNOTATION) != 0; + } + + public final void isAnnotation(final boolean flag) { + setFlag(Const.ACC_ANNOTATION, flag); + } + + public final boolean isEnum() { + return (access_flags & Const.ACC_ENUM) != 0; + } + + public final void isEnum(final boolean flag) { + setFlag(Const.ACC_ENUM, flag); + } + + public final boolean isFinal() { + return (access_flags & Const.ACC_FINAL) != 0; + } + + public final void isFinal(final boolean flag) { + setFlag(Const.ACC_FINAL, flag); + } + + public final boolean isInterface() { + return (access_flags & Const.ACC_INTERFACE) != 0; + } + + public final void isInterface(final boolean flag) { + setFlag(Const.ACC_INTERFACE, flag); + } + + public final boolean isNative() { + return (access_flags & Const.ACC_NATIVE) != 0; + } + + public final void isNative(final boolean flag) { + setFlag(Const.ACC_NATIVE, flag); + } + + public final boolean isPrivate() { + return (access_flags & Const.ACC_PRIVATE) != 0; + } + + public final void isPrivate(final boolean flag) { + setFlag(Const.ACC_PRIVATE, flag); + } + + public final boolean isProtected() { + return (access_flags & Const.ACC_PROTECTED) != 0; + } + + public final void isProtected(final boolean flag) { + setFlag(Const.ACC_PROTECTED, flag); + } + + public final boolean isPublic() { + return (access_flags & Const.ACC_PUBLIC) != 0; + } + + public final void isPublic(final boolean flag) { + setFlag(Const.ACC_PUBLIC, flag); + } + + public final boolean isStatic() { + return (access_flags & Const.ACC_STATIC) != 0; + } + + public final void isStatic(final boolean flag) { + setFlag(Const.ACC_STATIC, flag); + } + + public final boolean isStrictfp() { + return (access_flags & Const.ACC_STRICT) != 0; + } + + public final void isStrictfp(final boolean flag) { + setFlag(Const.ACC_STRICT, flag); + } + + public final boolean isSynchronized() { + return (access_flags & Const.ACC_SYNCHRONIZED) != 0; + } + + public final void isSynchronized(final boolean flag) { + setFlag(Const.ACC_SYNCHRONIZED, flag); + } + + public final boolean isSynthetic() { + return (access_flags & Const.ACC_SYNTHETIC) != 0; + } + + public final void isSynthetic(final boolean flag) { + setFlag(Const.ACC_SYNTHETIC, flag); + } + + public final boolean isTransient() { + return (access_flags & Const.ACC_TRANSIENT) != 0; + } + + public final void isTransient(final boolean flag) { + setFlag(Const.ACC_TRANSIENT, flag); + } + + public final boolean isVarArgs() { + return (access_flags & Const.ACC_VARARGS) != 0; + } + + public final void isVarArgs(final boolean flag) { + setFlag(Const.ACC_VARARGS, flag); + } + + public final boolean isVolatile() { + return (access_flags & Const.ACC_VOLATILE) != 0; + } + + public final void isVolatile(final boolean flag) { + setFlag(Const.ACC_VOLATILE, flag); + } + + /** + * Set access flags aka "modifiers". + * + * @param accessFlags Access flags of the object. + */ + public final void setAccessFlags(final int accessFlags) { + this.access_flags = accessFlags; + } + + private void setFlag(final int flag, final boolean set) { + if ((access_flags & flag) != 0) { // Flag is set already + if (!set) { + access_flags ^= flag; + } + } else if (set) { + access_flags |= flag; + } + } + + /** + * Set access flags aka "modifiers". + * + * @param accessFlags Access flags of the object. + */ + public final void setModifiers(final int accessFlags) { + setAccessFlags(accessFlags); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/AnnotationDefault.java b/src/main/java/haidnor/jvm/bcel/classfile/AnnotationDefault.java new file mode 100644 index 0000000..c28cb33 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/AnnotationDefault.java @@ -0,0 +1,92 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Represents the default value of a annotation for a method info. + * + * @since 6.0 + */ +public class AnnotationDefault extends Attribute { + + private ElementValue defaultValue; + + /** + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + */ + AnnotationDefault(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (ElementValue) null, constantPool); + defaultValue = ElementValue.readElementValue(input, constantPool); + } + + /** + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param defaultValue the annotation's default value + * @param constantPool Array of constants + */ + public AnnotationDefault(final int nameIndex, final int length, final ElementValue defaultValue, final ConstantPool constantPool) { + super(Const.ATTR_ANNOTATION_DEFAULT, nameIndex, length, constantPool); + this.defaultValue = defaultValue; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitAnnotationDefault(this); + } + + @Override + public Attribute copy(final ConstantPool constantPool) { + return (Attribute) clone(); + } + + @Override + public final void dump(final DataOutputStream dos) throws IOException { + super.dump(dos); + defaultValue.dump(dos); + } + + /** + * @return the default value + */ + public final ElementValue getDefaultValue() { + return defaultValue; + } + + /** + * @param defaultValue the default value of this methodinfo's annotation + */ + public final void setDefaultValue(final ElementValue defaultValue) { + this.defaultValue = defaultValue; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/AnnotationElementValue.java b/src/main/java/haidnor/jvm/bcel/classfile/AnnotationElementValue.java new file mode 100644 index 0000000..68bdd0f --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/AnnotationElementValue.java @@ -0,0 +1,56 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * @since 6.0 + */ +public class AnnotationElementValue extends ElementValue { + // For annotation element values, this is the annotation + private final AnnotationEntry annotationEntry; + + public AnnotationElementValue(final int type, final AnnotationEntry annotationEntry, final ConstantPool cpool) { + super(type, cpool); + if (type != ANNOTATION) { + throw new ClassFormatException("Only element values of type annotation can be built with this ctor - type specified: " + type); + } + this.annotationEntry = annotationEntry; + } + + @Override + public void dump(final DataOutputStream dos) throws IOException { + dos.writeByte(super.getType()); // u1 type of value (ANNOTATION == '@') + annotationEntry.dump(dos); + } + + public AnnotationEntry getAnnotationEntry() { + return annotationEntry; + } + + @Override + public String stringifyValue() { + return annotationEntry.toString(); + } + + @Override + public String toString() { + return stringifyValue(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/AnnotationEntry.java b/src/main/java/haidnor/jvm/bcel/classfile/AnnotationEntry.java new file mode 100644 index 0000000..f2776ac --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/AnnotationEntry.java @@ -0,0 +1,162 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +/** + * Represents one annotation in the annotation table + * + * @since 6.0 + */ +public class AnnotationEntry implements Node { + + public static final AnnotationEntry[] EMPTY_ARRAY = {}; + + public static AnnotationEntry[] createAnnotationEntries(final Attribute[] attrs) { + // Find attributes that contain annotation data + return Stream.of(attrs).filter(Annotations.class::isInstance).flatMap(e -> Stream.of(((Annotations) e).getAnnotationEntries())) + .toArray(AnnotationEntry[]::new); + } + + /** + * Factory method to create an AnnotionEntry from a DataInput + * + * @param input + * @param constantPool + * @param isRuntimeVisible + * @return the entry + * @throws IOException if an I/O error occurs. + */ + public static AnnotationEntry read(final DataInput input, final ConstantPool constantPool, final boolean isRuntimeVisible) throws IOException { + final AnnotationEntry annotationEntry = new AnnotationEntry(input.readUnsignedShort(), constantPool, isRuntimeVisible); + final int numElementValuePairs = input.readUnsignedShort(); + annotationEntry.elementValuePairs = new ArrayList<>(); + for (int i = 0; i < numElementValuePairs; i++) { + annotationEntry.elementValuePairs + .add(new ElementValuePair(input.readUnsignedShort(), ElementValue.readElementValue(input, constantPool), constantPool)); + } + return annotationEntry; + } + + private final int typeIndex; + + private final ConstantPool constantPool; + + private final boolean isRuntimeVisible; + + private List elementValuePairs; + + public AnnotationEntry(final int typeIndex, final ConstantPool constantPool, final boolean isRuntimeVisible) { + this.typeIndex = typeIndex; + this.constantPool = constantPool; + this.isRuntimeVisible = isRuntimeVisible; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitAnnotationEntry(this); + } + + public void addElementNameValuePair(final ElementValuePair elementNameValuePair) { + elementValuePairs.add(elementNameValuePair); + } + + public void dump(final DataOutputStream dos) throws IOException { + dos.writeShort(typeIndex); // u2 index of type name in cpool + dos.writeShort(elementValuePairs.size()); // u2 element_value pair + // count + for (final ElementValuePair envp : elementValuePairs) { + envp.dump(dos); + } + } + + /** + * @return the annotation type name + */ + public String getAnnotationType() { + return constantPool.getConstantUtf8(typeIndex).getBytes(); + } + + /** + * @return the annotation type index + */ + public int getAnnotationTypeIndex() { + return typeIndex; + } + + public ConstantPool getConstantPool() { + return constantPool; + } + + /** + * @return the element value pairs in this annotation entry + */ + public ElementValuePair[] getElementValuePairs() { + // TODO return List + return elementValuePairs.toArray(ElementValuePair.EMPTY_ARRAY); + } + + /** + * @return the number of element value pairs in this annotation entry + */ + public final int getNumElementValuePairs() { + return elementValuePairs.size(); + } + + public int getTypeIndex() { + return typeIndex; + } + + public boolean isRuntimeVisible() { + return isRuntimeVisible; + } + + public String toShortString() { + final StringBuilder result = new StringBuilder(); + result.append("@"); + result.append(getAnnotationType()); + final ElementValuePair[] evPairs = getElementValuePairs(); + if (evPairs.length > 0) { + result.append("("); + for (final ElementValuePair element : evPairs) { + result.append(element.toShortString()); + result.append(", "); + } + // remove last ", " + result.setLength(result.length() - 2); + result.append(")"); + } + return result.toString(); + } + + @Override + public String toString() { + return toShortString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Annotations.java b/src/main/java/haidnor/jvm/bcel/classfile/Annotations.java new file mode 100644 index 0000000..a2fe3c4 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Annotations.java @@ -0,0 +1,158 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.stream.Stream; + +/** + * base class for annotations + * + * @since 6.0 + */ +public abstract class Annotations extends Attribute implements Iterable { + + private AnnotationEntry[] annotationTable; + private final boolean isRuntimeVisible; + + /** + * Constructs an instance. + * + * @param annotationType the subclass type of the annotation + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param annotationTable the actual annotations + * @param constantPool Array of constants + * @param isRuntimeVisible whether this Annotation visible at runtime + */ + public Annotations(final byte annotationType, final int nameIndex, final int length, final AnnotationEntry[] annotationTable, + final ConstantPool constantPool, final boolean isRuntimeVisible) { + super(annotationType, nameIndex, length, constantPool); + this.annotationTable = annotationTable; + this.isRuntimeVisible = isRuntimeVisible; + } + + /** + * Constructs an instance. + * + * @param annotationType the subclass type of the annotation + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @param isRuntimeVisible whether this Annotation visible at runtime + * @throws IOException if an I/O error occurs. + */ + Annotations(final byte annotationType, final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool, + final boolean isRuntimeVisible) throws IOException { + this(annotationType, nameIndex, length, (AnnotationEntry[]) null, constantPool, isRuntimeVisible); + final int annotationTableLength = input.readUnsignedShort(); + annotationTable = new AnnotationEntry[annotationTableLength]; + for (int i = 0; i < annotationTableLength; i++) { + annotationTable[i] = AnnotationEntry.read(input, constantPool, isRuntimeVisible); + } + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly + * defined by the contents of a Java class. I.e., the hierarchy of methods, + * fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitAnnotation(this); + } + + @Override + public Attribute copy(final ConstantPool constantPool) { + // TODO Auto-generated method stub + return null; + } + + /** + * Gets the array of annotation entries in this annotation + */ + public AnnotationEntry[] getAnnotationEntries() { + return annotationTable; + } + + /** + * Gets the number of annotation entries in this annotation. + * + * @return the number of annotation entries in this annotation + */ + public final int getNumAnnotations() { + if (annotationTable == null) { + return 0; + } + return annotationTable.length; + } + + public boolean isRuntimeVisible() { + return isRuntimeVisible; + } + + @Override + public Iterator iterator() { + return Stream.of(annotationTable).iterator(); + } + + /** + * Sets the entries to set in this annotation. + * + * @param annotationTable the entries to set in this annotation + */ + public final void setAnnotationTable(final AnnotationEntry[] annotationTable) { + this.annotationTable = annotationTable; + } + + /** + * Converts to a String representation. + * + * @return String representation + */ + @Override + public final String toString() { + final StringBuilder buf = new StringBuilder(Const.getAttributeName(getTag())); + buf.append(":\n"); + for (int i = 0; i < annotationTable.length; i++) { + buf.append(" ").append(annotationTable[i]); + if (i < annotationTable.length - 1) { + buf.append('\n'); + } + } + return buf.toString(); + } + + protected void writeAnnotations(final DataOutputStream dos) throws IOException { + if (annotationTable == null) { + return; + } + dos.writeShort(annotationTable.length); + for (final AnnotationEntry element : annotationTable) { + element.dump(dos); + } + } + +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ArrayElementValue.java b/src/main/java/haidnor/jvm/bcel/classfile/ArrayElementValue.java new file mode 100644 index 0000000..c3db238 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ArrayElementValue.java @@ -0,0 +1,81 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * @since 6.0 + */ +public class ArrayElementValue extends ElementValue { + // For array types, this is the array + private final ElementValue[] elementValues; + + public ArrayElementValue(final int type, final ElementValue[] datums, final ConstantPool cpool) { + super(type, cpool); + if (type != ARRAY) { + throw new ClassFormatException("Only element values of type array can be built with this ctor - type specified: " + type); + } + this.elementValues = datums; + } + + @Override + public void dump(final DataOutputStream dos) throws IOException { + dos.writeByte(super.getType()); // u1 type of value (ARRAY == '[') + dos.writeShort(elementValues.length); + for (final ElementValue evalue : elementValues) { + evalue.dump(dos); + } + } + + public ElementValue[] getElementValuesArray() { + return elementValues; + } + + public int getElementValuesArraySize() { + return elementValues.length; + } + + @Override + public String stringifyValue() { + final StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < elementValues.length; i++) { + sb.append(elementValues[i].stringifyValue()); + if (i + 1 < elementValues.length) { + sb.append(","); + } + } + sb.append("]"); + return sb.toString(); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (int i = 0; i < elementValues.length; i++) { + sb.append(elementValues[i]); + if (i + 1 < elementValues.length) { + sb.append(","); + } + } + sb.append("}"); + return sb.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Attribute.java b/src/main/java/haidnor/jvm/bcel/classfile/Attribute.java new file mode 100644 index 0000000..b983150 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Attribute.java @@ -0,0 +1,367 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Abstract super class for Attribute objects. Currently the ConstantValue, SourceFile, Code, Exceptiontable, + * LineNumberTable, LocalVariableTable, InnerClasses and Synthetic attributes are supported. The Unknown attribute + * stands for non-standard-attributes. + * + *
+ * attribute_info {
+ *   u2 attribute_name_index;
+ *   u4 attribute_length;
+ *   u1 info[attribute_length];
+ * }
+ * 
+ * + * @see ConstantValue + * @see SourceFile + * @see Code + * @see Unknown + * @see ExceptionTable + * @see LineNumberTable + * @see LocalVariableTable + * @see InnerClasses + * @see Synthetic + * @see Deprecated + * @see Signature + */ +public abstract class Attribute implements Cloneable, Node { + + private static final boolean debug = Boolean.getBoolean(Attribute.class.getCanonicalName() + ".debug"); // Debugging on/off + + private static final Map READERS = new HashMap<>(); + + /** + * Empty array. + * + * @since 6.6.0 + */ + public static final Attribute[] EMPTY_ARRAY = {}; + + /** + * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the + * standard attributes such as "LineNumberTable", because those are handled internally. + * + * @param name the name of the attribute as stored in the class file + * @param unknownAttributeReader the reader object + */ + public static void addAttributeReader(final String name, final UnknownAttributeReader unknownAttributeReader) { + READERS.put(name, unknownAttributeReader); + } + + protected static void println(final String msg) { + if (debug) { + System.err.println(msg); + } + } + + /** + * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It + * is called by the Field and Method constructor methods. + * + * @see JavaField + * @see JavaMethod + * + * @param dataInput Input stream + * @param constantPool Array of constants + * @return Attribute + * @throws IOException if an I/O error occurs. + * @since 6.0 + */ + public static Attribute readAttribute(final DataInput dataInput, final ConstantPool constantPool) throws IOException { + byte tag = Const.ATTR_UNKNOWN; // Unknown attribute + // Get class name from constant pool via 'name_index' indirection + final int nameIndex = dataInput.readUnsignedShort(); + final String name = constantPool.getConstantUtf8(nameIndex).getBytes(); + + // Length of data in bytes + final int length = dataInput.readInt(); + + // Compare strings to find known attribute + for (byte i = 0; i < Const.KNOWN_ATTRIBUTES; i++) { + if (name.equals(Const.getAttributeName(i))) { + tag = i; // found! + break; + } + } + + // Call proper constructor, depending on 'tag' + switch (tag) { + case Const.ATTR_UNKNOWN: + final Object r = READERS.get(name); + if (r instanceof UnknownAttributeReader) { + return ((UnknownAttributeReader) r).createAttribute(nameIndex, length, dataInput, constantPool); + } + return new Unknown(nameIndex, length, dataInput, constantPool); + case Const.ATTR_CONSTANT_VALUE: + return new ConstantValue(nameIndex, length, dataInput, constantPool); + case Const.ATTR_SOURCE_FILE: + return new SourceFile(nameIndex, length, dataInput, constantPool); + case Const.ATTR_CODE: + return new Code(nameIndex, length, dataInput, constantPool); + case Const.ATTR_EXCEPTIONS: + return new ExceptionTable(nameIndex, length, dataInput, constantPool); + case Const.ATTR_LINE_NUMBER_TABLE: + return new LineNumberTable(nameIndex, length, dataInput, constantPool); + case Const.ATTR_LOCAL_VARIABLE_TABLE: + return new LocalVariableTable(nameIndex, length, dataInput, constantPool); + case Const.ATTR_INNER_CLASSES: + return new InnerClasses(nameIndex, length, dataInput, constantPool); + case Const.ATTR_SYNTHETIC: + return new Synthetic(nameIndex, length, dataInput, constantPool); + case Const.ATTR_DEPRECATED: + return new Deprecated(nameIndex, length, dataInput, constantPool); + case Const.ATTR_PMG: + return new PMGClass(nameIndex, length, dataInput, constantPool); + case Const.ATTR_SIGNATURE: + return new Signature(nameIndex, length, dataInput, constantPool); + case Const.ATTR_STACK_MAP: + // old style stack map: unneeded for JDK5 and below; + // illegal(?) for JDK6 and above. So just delete with a warning. + println("Warning: Obsolete StackMap attribute ignored."); + return new Unknown(nameIndex, length, dataInput, constantPool); + case Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS: + return new RuntimeVisibleAnnotations(nameIndex, length, dataInput, constantPool); + case Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS: + return new RuntimeInvisibleAnnotations(nameIndex, length, dataInput, constantPool); + case Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS: + return new RuntimeVisibleParameterAnnotations(nameIndex, length, dataInput, constantPool); + case Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS: + return new RuntimeInvisibleParameterAnnotations(nameIndex, length, dataInput, constantPool); + case Const.ATTR_ANNOTATION_DEFAULT: + return new AnnotationDefault(nameIndex, length, dataInput, constantPool); + case Const.ATTR_LOCAL_VARIABLE_TYPE_TABLE: + return new LocalVariableTypeTable(nameIndex, length, dataInput, constantPool); + case Const.ATTR_ENCLOSING_METHOD: + return new EnclosingMethod(nameIndex, length, dataInput, constantPool); + case Const.ATTR_STACK_MAP_TABLE: + // read new style stack map: StackMapTable. The rest of the code + // calls this a StackMap for historical reasons. + return new StackMap(nameIndex, length, dataInput, constantPool); + case Const.ATTR_BOOTSTRAP_METHODS: + return new BootstrapMethods(nameIndex, length, dataInput, constantPool); + case Const.ATTR_METHOD_PARAMETERS: + return new MethodParameters(nameIndex, length, dataInput, constantPool); + case Const.ATTR_MODULE: + return new Module(nameIndex, length, dataInput, constantPool); + case Const.ATTR_MODULE_PACKAGES: + return new ModulePackages(nameIndex, length, dataInput, constantPool); + case Const.ATTR_MODULE_MAIN_CLASS: + return new ModuleMainClass(nameIndex, length, dataInput, constantPool); + case Const.ATTR_NEST_HOST: + return new NestHost(nameIndex, length, dataInput, constantPool); + case Const.ATTR_NEST_MEMBERS: + return new NestMembers(nameIndex, length, dataInput, constantPool); + default: + // Never reached + throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag); + } + } + + /** + * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It + * is called by the Field and Method constructor methods. + * + * @see JavaField + * @see JavaMethod + * + * @param dataInputStream Input stream + * @param constantPool Array of constants + * @return Attribute + * @throws IOException if an I/O error occurs. + */ + public static Attribute readAttribute(final DataInputStream dataInputStream, final ConstantPool constantPool) throws IOException { + return readAttribute((DataInput) dataInputStream, constantPool); + } + + /** + * Remove attribute reader + * + * @param name the name of the attribute as stored in the class file + */ + public static void removeAttributeReader(final String name) { + READERS.remove(name); + } + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected int name_index; // Points to attribute name in constant pool TODO make private (has getter & setter) + + /** + * @deprecated (since 6.0) (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected int length; // Content length of attribute field TODO make private (has getter & setter) + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected byte tag; // Tag to distinguish subclasses TODO make private & final; supposed to be immutable + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected ConstantPool constant_pool; // TODO make private (has getter & setter) + + /** + * Constructs an instance. + * + *
+     * attribute_info {
+     *   u2 attribute_name_index;
+     *   u4 attribute_length;
+     *   u1 info[attribute_length];
+     * }
+     * 
+ * + * @param tag tag. + * @param nameIndex u2 name index. + * @param length u4 length. + * @param constantPool constant pool. + */ + protected Attribute(final byte tag, final int nameIndex, final int length, final ConstantPool constantPool) { + this.tag = tag; + this.name_index = Args.requireU2(nameIndex, 0, constantPool.getLength(), getClass().getSimpleName() + " name index"); + this.length = Args.requireU4(length, getClass().getSimpleName() + " attribute length"); + this.constant_pool = constantPool; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public abstract void accept(Visitor v); + + /** + * Use copy() if you want to have a deep copy(), i.e., with all references copied correctly. + * + * @return shallow copy of this attribute + */ + @Override + public Object clone() { + Attribute attr = null; + try { + attr = (Attribute) super.clone(); + } catch (final CloneNotSupportedException e) { + throw new Error("Clone Not Supported"); // never happens + } + return attr; + } + + /** + * @param constantPool constant pool to save. + * @return deep copy of this attribute. + */ + public abstract Attribute copy(ConstantPool constantPool); + + /** + * Dumps attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + public void dump(final DataOutputStream file) throws IOException { + file.writeShort(name_index); + file.writeInt(length); + } + + /** + * @return Constant pool used by this object. + * @see ConstantPool + */ + public final ConstantPool getConstantPool() { + return constant_pool; + } + + /** + * @return Length of attribute field in bytes. + */ + public final int getLength() { + return length; + } + + /** + * @return Name of attribute + * @since 6.0 + */ + public String getName() { + return constant_pool.getConstantUtf8(name_index).getBytes(); + } + + /** + * @return Name index in constant pool of attribute name. + */ + public final int getNameIndex() { + return name_index; + } + + /** + * @return Tag of attribute, i.e., its type. Value may not be altered, thus there is no setTag() method. + */ + public final byte getTag() { + return tag; + } + + /** + * @param constantPool Constant pool to be used for this object. + * @see ConstantPool + */ + public final void setConstantPool(final ConstantPool constantPool) { + this.constant_pool = constantPool; + } + + /** + * @param length length in bytes. + */ + public final void setLength(final int length) { + this.length = length; + } + + /** + * @param nameIndex of attribute. + */ + public final void setNameIndex(final int nameIndex) { + this.name_index = nameIndex; + } + + /** + * @return attribute name. + */ + @Override + public String toString() { + return Const.getAttributeName(tag); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/BootstrapMethod.java b/src/main/java/haidnor/jvm/bcel/classfile/BootstrapMethod.java new file mode 100644 index 0000000..d310548 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/BootstrapMethod.java @@ -0,0 +1,166 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * This class represents a bootstrap method attribute, i.e., the bootstrap method ref, the number of bootstrap arguments + * and an array of the bootstrap arguments. + * + * @see The class File Format : + * The BootstrapMethods Attribute + * @since 6.0 + */ +public class BootstrapMethod implements Cloneable { + + /** Index of the CONSTANT_MethodHandle_info structure in the constant_pool table */ + private int bootstrapMethodRef; + + /** Array of references to the constant_pool table */ + private int[] bootstrapArguments; + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public BootstrapMethod(final BootstrapMethod c) { + this(c.getBootstrapMethodRef(), c.getBootstrapArguments()); + } + + /** + * Construct object from input stream. + * + * @param input Input stream + * @throws IOException if an I/O error occurs. + */ + BootstrapMethod(final DataInput input) throws IOException { + this(input.readUnsignedShort(), input.readUnsignedShort()); + + for (int i = 0; i < bootstrapArguments.length; i++) { + bootstrapArguments[i] = input.readUnsignedShort(); + } + } + + // helper method + private BootstrapMethod(final int bootstrapMethodRef, final int numBootstrapArguments) { + this(bootstrapMethodRef, new int[numBootstrapArguments]); + } + + /** + * @param bootstrapMethodRef int index into constant_pool of CONSTANT_MethodHandle + * @param bootstrapArguments int[] indices into constant_pool of CONSTANT_[type]_info + */ + public BootstrapMethod(final int bootstrapMethodRef, final int[] bootstrapArguments) { + this.bootstrapMethodRef = bootstrapMethodRef; + this.bootstrapArguments = bootstrapArguments; + } + + /** + * @return deep copy of this object + */ + public BootstrapMethod copy() { + try { + return (BootstrapMethod) clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + /** + * Dump object to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + public final void dump(final DataOutputStream file) throws IOException { + file.writeShort(bootstrapMethodRef); + file.writeShort(bootstrapArguments.length); + for (final int bootstrapArgument : bootstrapArguments) { + file.writeShort(bootstrapArgument); + } + } + + /** + * @return int[] of bootstrap_method indices into constant_pool of CONSTANT_[type]_info + */ + public int[] getBootstrapArguments() { + return bootstrapArguments; + } + + /** + * @return index into constant_pool of bootstrap_method + */ + public int getBootstrapMethodRef() { + return bootstrapMethodRef; + } + + /** + * @return count of number of boostrap arguments + */ + public int getNumBootstrapArguments() { + return bootstrapArguments.length; + } + + /** + * @param bootstrapArguments int[] indices into constant_pool of CONSTANT_[type]_info + */ + public void setBootstrapArguments(final int[] bootstrapArguments) { + this.bootstrapArguments = bootstrapArguments; + } + + /** + * @param bootstrapMethodRef int index into constant_pool of CONSTANT_MethodHandle + */ + public void setBootstrapMethodRef(final int bootstrapMethodRef) { + this.bootstrapMethodRef = bootstrapMethodRef; + } + + /** + * @return String representation. + */ + @Override + public final String toString() { + return "BootstrapMethod(" + bootstrapMethodRef + ", " + bootstrapArguments.length + ", " + Arrays.toString(bootstrapArguments) + ")"; + } + + /** + * @return Resolved string representation + */ + public final String toString(final ConstantPool constantPool) { + final StringBuilder buf = new StringBuilder(); + final String bootstrapMethodName = constantPool.constantToString(bootstrapMethodRef, Const.CONSTANT_MethodHandle); + buf.append(Utility.compactClassName(bootstrapMethodName, false)); + final int bootstrapArgumentsLen = bootstrapArguments.length; + if (bootstrapArgumentsLen > 0) { + buf.append("\nMethod Arguments:"); + for (int i = 0; i < bootstrapArgumentsLen; i++) { + buf.append("\n ").append(i).append(": "); + buf.append(constantPool.constantToString(constantPool.getConstant(bootstrapArguments[i]))); + } + } + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/BootstrapMethods.java b/src/main/java/haidnor/jvm/bcel/classfile/BootstrapMethods.java new file mode 100644 index 0000000..fa94a19 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/BootstrapMethods.java @@ -0,0 +1,159 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.stream.Stream; + +/** + * This class represents a BootstrapMethods attribute. + * + * @see The class File Format : + * The BootstrapMethods Attribute + * @since 6.0 + */ +public class BootstrapMethods extends Attribute implements Iterable { + + private BootstrapMethod[] bootstrapMethods; // TODO this could be made final (setter is not used) + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a + * physical copy. + * + * @param c Source to copy. + */ + public BootstrapMethods(final BootstrapMethods c) { + this(c.getNameIndex(), c.getLength(), c.getBootstrapMethods(), c.getConstantPool()); + } + + /** + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param bootstrapMethods array of bootstrap methods + * @param constantPool Array of constants + */ + public BootstrapMethods(final int nameIndex, final int length, final BootstrapMethod[] bootstrapMethods, final ConstantPool constantPool) { + super(Const.ATTR_BOOTSTRAP_METHODS, nameIndex, length, constantPool); + this.bootstrapMethods = bootstrapMethods; + } + + /** + * Construct object from Input stream. + * + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + BootstrapMethods(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (BootstrapMethod[]) null, constantPool); + + final int numBootstrapMethods = input.readUnsignedShort(); + bootstrapMethods = new BootstrapMethod[numBootstrapMethods]; + for (int i = 0; i < numBootstrapMethods; i++) { + bootstrapMethods[i] = new BootstrapMethod(input); + } + } + + /** + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitBootstrapMethods(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public BootstrapMethods copy(final ConstantPool constantPool) { + final BootstrapMethods c = (BootstrapMethods) clone(); + c.bootstrapMethods = new BootstrapMethod[bootstrapMethods.length]; + + for (int i = 0; i < bootstrapMethods.length; i++) { + c.bootstrapMethods[i] = bootstrapMethods[i].copy(); + } + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump bootstrap methods attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public final void dump(final DataOutputStream file) throws IOException { + super.dump(file); + + file.writeShort(bootstrapMethods.length); + for (final BootstrapMethod bootstrapMethod : bootstrapMethods) { + bootstrapMethod.dump(file); + } + } + + /** + * @return array of bootstrap method "records" + */ + public final BootstrapMethod[] getBootstrapMethods() { + return bootstrapMethods; + } + + @Override + public Iterator iterator() { + return Stream.of(bootstrapMethods).iterator(); + } + + /** + * @param bootstrapMethods the array of bootstrap methods + */ + public final void setBootstrapMethods(final BootstrapMethod[] bootstrapMethods) { + this.bootstrapMethods = bootstrapMethods; + } + + /** + * @return String representation. + */ + @Override + public final String toString() { + final StringBuilder buf = new StringBuilder(); + buf.append("BootstrapMethods("); + buf.append(bootstrapMethods.length); + buf.append("):"); + for (int i = 0; i < bootstrapMethods.length; i++) { + buf.append("\n"); + final int start = buf.length(); + buf.append(" ").append(i).append(": "); + final int indentCount = buf.length() - start; + final String[] lines = bootstrapMethods[i].toString(super.getConstantPool()).split("\\r?\\n"); + buf.append(lines[0]); + for (int j = 1; j < lines.length; j++) { + buf.append("\n").append(" ", 0, indentCount).append(lines[j]); + } + } + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ClassElementValue.java b/src/main/java/haidnor/jvm/bcel/classfile/ClassElementValue.java new file mode 100644 index 0000000..4120d0e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ClassElementValue.java @@ -0,0 +1,54 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * @since 6.0 + */ +public class ClassElementValue extends ElementValue { + // For primitive types and string type, this points to the value entry in + // the cpool + // For 'class' this points to the class entry in the cpool + private final int idx; + + public ClassElementValue(final int type, final int idx, final ConstantPool cpool) { + super(type, cpool); + this.idx = idx; + } + + @Override + public void dump(final DataOutputStream dos) throws IOException { + dos.writeByte(super.getType()); // u1 kind of value + dos.writeShort(idx); + } + + public String getClassString() { + return super.getConstantPool().getConstantUtf8(idx).getBytes(); + } + + public int getIndex() { + return idx; + } + + @Override + public String stringifyValue() { + return super.getConstantPool().getConstantUtf8(idx).getBytes(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ClassFormatException.java b/src/main/java/haidnor/jvm/bcel/classfile/ClassFormatException.java new file mode 100644 index 0000000..d0e2504 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ClassFormatException.java @@ -0,0 +1,68 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +/** + * Thrown when the BCEL attempts to read a class file and determines that a class is malformed or otherwise cannot be interpreted as a class file. + */ +public class ClassFormatException extends RuntimeException { + + private static final long serialVersionUID = -3569097343160139969L; + + /** + * Constructs a new instance with {@code null} as its detail message. The cause is not initialized, and may subsequently be initialized by a call to + * {@link #initCause}. + */ + public ClassFormatException() { + } + + /** + * Constructs a new instance with the specified detail message. The cause is not initialized, and may subsequently be initialized by a call to + * {@link #initCause}. + * + * @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method. + */ + public ClassFormatException(final String message) { + super(message); + } + + /** + * Constructs a new instance with the specified detail message and cause. + *

+ * Note that the detail message associated with {@code cause} is not automatically incorporated in this runtime exception's detail message. + * + * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). (A {@code null} value is permitted, and indicates that + * the cause is nonexistent or unknown.) + * @since 6.0 + */ + public ClassFormatException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new instance with the specified cause and a detail message of {@code (cause==null ? null : cause.toString())} (which typically contains the + * class and detail message of {@code cause}). This constructor is useful for runtime exceptions that are little more than wrappers for other throwables. + * + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). (A {@code null} value is permitted, and indicates that the + * cause is nonexistent or unknown.) + * @since 6.7.0 + */ + public ClassFormatException(final Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ClassParser.java b/src/main/java/haidnor/jvm/bcel/classfile/ClassParser.java new file mode 100644 index 0000000..e1b7cc3 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ClassParser.java @@ -0,0 +1,288 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Wrapper class that parses a given Java .class file. The method parse returns a + * JavaClass object on success. When an I/O error or an inconsistency occurs an + * appropriate exception is propagated back to the caller. + * + * The structure and the names comply, except for a few conveniences, exactly with the + * JVM specification 1.0. See this paper for further details about + * the structure of a bytecode file. + */ +public final class ClassParser { + + private static final int BUFSIZE = 8192; + private DataInputStream dataInputStream; + private final boolean fileOwned; + private final String fileName; + private String zipFile; + private int classNameIndex; + private int superclassNameIndex; + private int major; // Compiler version + private int minor; // Compiler version + private int accessFlags; // Access rights of parsed class + private int[] interfaces; // Names of implemented interfaces + private ConstantPool constantPool; // collection of constants + private JavaField[] fields; // class fields, i.e., its variables + private JavaMethod[] methods; // methods defined in the class + private Attribute[] attributes; // attributes defined in the class + private final boolean isZip; // Loaded from zip file + + /** + * Parses class from the given stream. + * + * @param inputStream Input stream + * @param fileName File name + */ + public ClassParser(final InputStream inputStream, final String fileName) { + this.fileName = fileName; + this.fileOwned = false; + final String clazz = inputStream.getClass().getName(); // Not a very clean solution ... + this.isZip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar."); + if (inputStream instanceof DataInputStream) { + this.dataInputStream = (DataInputStream) inputStream; + } else { + this.dataInputStream = new DataInputStream(new BufferedInputStream(inputStream, BUFSIZE)); + } + } + + /** + * Parses class from given .class file. + * + * @param fileName file name + */ + public ClassParser(final String fileName) { + this.isZip = false; + this.fileName = fileName; + this.fileOwned = true; + } + + /** + * Parses class from given .class file in a ZIP-archive + * + * @param zipFile zip file name + * @param fileName file name + */ + public ClassParser(final String zipFile, final String fileName) { + this.isZip = true; + this.fileOwned = true; + this.zipFile = zipFile; + this.fileName = fileName; + } + + /** + * Parses the given Java class file and return an object that represents the contained data, i.e., constants, methods, + * fields and commands. A ClassFormatException is raised, if the file is not a valid .class file. (This does + * not include verification of the byte code as it is performed by the java interpreter). + * + * @return Class object representing the parsed class file + * @throws IOException if an I/O error occurs. + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + public JavaClass parse() throws IOException, ClassFormatException { + ZipFile zip = null; + try { + if (fileOwned) { + if (isZip) { + zip = new ZipFile(zipFile); + final ZipEntry entry = zip.getEntry(fileName); + + if (entry == null) { + throw new IOException("File " + fileName + " not found"); + } + + dataInputStream = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry), BUFSIZE)); + } else { + dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(fileName), BUFSIZE)); + } + } + /****************** Read headers ********************************/ + // Check magic tag of class file + readID(); + // Get compiler version + readVersion(); + /****************** Read constant pool and related **************/ + // Read constant pool entries + readConstantPool(); + // Get class information + readClassInfo(); + // Get interface information, i.e., implemented interfaces + readInterfaces(); + /****************** Read class fields and methods ***************/ + // Read class fields, i.e., the variables of the class + readFields(); + // Read class methods, i.e., the functions in the class + readMethods(); + // Read class attributes + readAttributes(); + // Check for unknown variables + // Unknown[] u = Unknown.getUnknownAttributes(); + // for (int i=0; i < u.length; i++) + // System.err.println("WARNING: " + u[i]); + // Everything should have been read now + // if(file.available() > 0) { + // int bytes = file.available(); + // byte[] buf = new byte[bytes]; + // file.read(buf); + // if(!(isZip && (buf.length == 1))) { + // System.err.println("WARNING: Trailing garbage at end of " + fileName); + // System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf)); + // } + // } + } finally { + // Read everything of interest, so close the file + if (fileOwned) { + try { + if (dataInputStream != null) { + dataInputStream.close(); + } + } catch (final IOException ignored) { + // ignore close exceptions + } + } + try { + if (zip != null) { + zip.close(); + } + } catch (final IOException ignored) { + // ignore close exceptions + } + } + // Return the information we have gathered in a new object + return new JavaClass(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes, + isZip ? JavaClass.ZIP : JavaClass.FILE); + } + + /** + * Reads information about the attributes of the class. + * + * @throws IOException if an I/O error occurs. + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + private void readAttributes() throws IOException, ClassFormatException { + final int attributesCount = dataInputStream.readUnsignedShort(); + attributes = new Attribute[attributesCount]; + for (int i = 0; i < attributesCount; i++) { + attributes[i] = Attribute.readAttribute(dataInputStream, constantPool); + } + } + + /** + * Reads information about the class and its super class. + * + * @throws IOException if an I/O error occurs. + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + private void readClassInfo() throws IOException, ClassFormatException { + accessFlags = dataInputStream.readUnsignedShort(); + /* + * Interfaces are implicitly abstract, the flag should be set according to the JVM specification. + */ + if ((accessFlags & Const.ACC_INTERFACE) != 0) { + accessFlags |= Const.ACC_ABSTRACT; + } + if ((accessFlags & Const.ACC_ABSTRACT) != 0 && (accessFlags & Const.ACC_FINAL) != 0) { + throw new ClassFormatException("Class " + fileName + " can't be both final and abstract"); + } + classNameIndex = dataInputStream.readUnsignedShort(); + superclassNameIndex = dataInputStream.readUnsignedShort(); + } + + /** + * Reads constant pool entries. + * + * @throws IOException if an I/O error occurs. + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + private void readConstantPool() throws IOException, ClassFormatException { + constantPool = new ConstantPool(dataInputStream); + } + + /** + * Reads information about the fields of the class, i.e., its variables. + * + * @throws IOException if an I/O error occurs. + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + private void readFields() throws IOException, ClassFormatException { + final int fieldsCount = dataInputStream.readUnsignedShort(); + fields = new JavaField[fieldsCount]; + for (int i = 0; i < fieldsCount; i++) { + fields[i] = new JavaField(dataInputStream, constantPool); + } + } + + /******************** Private utility methods **********************/ + /** + * Checks whether the header of the file is ok. Of course, this has to be the first action on successive file reads. + * + * @throws IOException if an I/O error occurs. + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + private void readID() throws IOException, ClassFormatException { + if (dataInputStream.readInt() != Const.JVM_CLASSFILE_MAGIC) { + throw new ClassFormatException(fileName + " is not a Java .class file"); + } + } + + /** + * Reads information about the interfaces implemented by this class. + * + * @throws IOException if an I/O error occurs. + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + private void readInterfaces() throws IOException, ClassFormatException { + final int interfacesCount = dataInputStream.readUnsignedShort(); + interfaces = new int[interfacesCount]; + for (int i = 0; i < interfacesCount; i++) { + interfaces[i] = dataInputStream.readUnsignedShort(); + } + } + + /** + * Reads information about the methods of the class. + * + * @throws IOException if an I/O error occurs. + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + private void readMethods() throws IOException { + final int methodsCount = dataInputStream.readUnsignedShort(); + methods = new JavaMethod[methodsCount]; + for (int i = 0; i < methodsCount; i++) { + methods[i] = new JavaMethod(dataInputStream, constantPool); + } + } + + /** + * Reads major and minor version of compiler which created the file. + * + * @throws IOException if an I/O error occurs. + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + private void readVersion() throws IOException, ClassFormatException { + minor = dataInputStream.readUnsignedShort(); + major = dataInputStream.readUnsignedShort(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Code.java b/src/main/java/haidnor/jvm/bcel/classfile/Code.java new file mode 100644 index 0000000..ef0d913 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Code.java @@ -0,0 +1,347 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; +import org.apache.commons.lang3.ArrayUtils; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * This class represents a chunk of Java byte code contained in a method. It is instantiated by the + * Attribute.readAttribute() method. A Code attribute contains informations about operand stack, local + * variables, byte code and the exceptions handled within this method. + * + * This attribute has attributes itself, namely LineNumberTable which is used for debugging purposes and + * LocalVariableTable which contains information about the local variables. + * + *

+ * Code_attribute {
+ *   u2 attribute_name_index;
+ *   u4 attribute_length;
+ *   u2 max_stack;
+ *   u2 max_locals;
+ *   u4 code_length;
+ *   u1 code[code_length];
+ *   u2 exception_table_length;
+ *   {
+ *     u2 start_pc;
+ *     u2 end_pc;
+ *     u2 handler_pc;
+ *     u2 catch_type;
+ *   } exception_table[exception_table_length];
+ *   u2 attributes_count;
+ *   attribute_info attributes[attributes_count];
+ * }
+ * 
+ * @see Attribute + * @see CodeException + * @see LineNumberTable + * @see LocalVariableTable + */ +public final class Code extends Attribute { + + private int maxStack; // Maximum size of stack used by this method // TODO this could be made final (setter is not used) + private int maxLocals; // Number of local variables // TODO this could be made final (setter is not used) + private byte[] code; // Actual byte code + private CodeException[] exceptionTable; // Table of handled exceptions + private Attribute[] attributes; // or LocalVariable + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a + * physical copy. + * + * @param code The source Code. + */ + public Code(final Code code) { + this(code.getNameIndex(), code.getLength(), code.getMaxStack(), code.getMaxLocals(), code.getCode(), code.getExceptionTable(), code.getAttributes(), + code.getConstantPool()); + } + + /** + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param file Input stream + * @param constantPool Array of constants + */ + Code(final int nameIndex, final int length, final DataInput file, final ConstantPool constantPool) throws IOException { + // Initialize with some default values which will be overwritten later + this(nameIndex, length, file.readUnsignedShort(), file.readUnsignedShort(), (byte[]) null, (CodeException[]) null, (Attribute[]) null, constantPool); + final int codeLength = Args.requireU4(file.readInt(), 1, "Code length attribute"); + code = new byte[codeLength]; // Read byte code + file.readFully(code); + /* + * Read exception table that contains all regions where an exception handler is active, i.e., a try { ... } catch() + * block. + */ + final int exceptionTableLength = file.readUnsignedShort(); + exceptionTable = new CodeException[exceptionTableLength]; + for (int i = 0; i < exceptionTableLength; i++) { + exceptionTable[i] = new CodeException(file); + } + /* + * Read all attributes, currently 'LineNumberTable' and 'LocalVariableTable' + */ + final int attributesCount = file.readUnsignedShort(); + attributes = new Attribute[attributesCount]; + for (int i = 0; i < attributesCount; i++) { + attributes[i] = Attribute.readAttribute(file, constantPool); + } + /* + * Adjust length, because of setAttributes in this(), s.b. length is incorrect, because it didn't take the internal + * attributes into account yet! Very subtle bug, fixed in 3.1.1. + */ + super.setLength(length); + } + + /** + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param maxStack Maximum size of stack + * @param maxLocals Number of local variables + * @param code Actual byte code + * @param exceptionTable of handled exceptions + * @param attributes Attributes of code: LineNumber or LocalVariable + * @param constantPool Array of constants + */ + public Code(final int nameIndex, final int length, final int maxStack, final int maxLocals, final byte[] code, final CodeException[] exceptionTable, + final Attribute[] attributes, final ConstantPool constantPool) { + super(Const.ATTR_CODE, nameIndex, length, constantPool); + this.maxStack = Args.requireU2(maxStack, "maxStack"); + this.maxLocals = Args.requireU2(maxLocals, "maxLocals"); + this.code = code != null ? code : ArrayUtils.EMPTY_BYTE_ARRAY; + this.exceptionTable = exceptionTable != null ? exceptionTable : CodeException.EMPTY_CODE_EXCEPTION_ARRAY; + Args.requireU2(this.exceptionTable.length, "exceptionTable.length"); + this.attributes = attributes != null ? attributes : EMPTY_ARRAY; + super.setLength(calculateLength()); // Adjust length + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitCode(this); + } + + /** + * @return the full size of this code attribute, minus its first 6 bytes, including the size of all its contained + * attributes + */ + private int calculateLength() { + int len = 0; + if (attributes != null) { + for (final Attribute attribute : attributes) { + len += attribute.getLength() + 6 /* attribute header size */; + } + } + return len + getInternalLength(); + } + + /** + * @return deep copy of this attribute + * + * @param constantPool the constant pool to duplicate + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final Code c = (Code) clone(); + if (code != null) { + c.code = code.clone(); + } + c.setConstantPool(constantPool); + c.exceptionTable = new CodeException[exceptionTable.length]; + Arrays.setAll(c.exceptionTable, i -> exceptionTable[i].copy()); + c.attributes = new Attribute[attributes.length]; + Arrays.setAll(c.attributes, i -> attributes[i].copy(constantPool)); + return c; + } + + /** + * Dump code attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(maxStack); + file.writeShort(maxLocals); + file.writeInt(code.length); + file.write(code, 0, code.length); + file.writeShort(exceptionTable.length); + for (final CodeException exception : exceptionTable) { + exception.dump(file); + } + file.writeShort(attributes.length); + for (final Attribute attribute : attributes) { + attribute.dump(file); + } + } + + /** + * @return Collection of code attributes. + * @see Attribute + */ + public Attribute[] getAttributes() { + return attributes; + } + + /** + * @return Actual byte code of the method. + */ + public byte[] getCode() { + return code; + } + + /** + * @return Table of handled exceptions. + * @see CodeException + */ + public CodeException[] getExceptionTable() { + return exceptionTable; + } + + /** + * @return the internal length of this code attribute (minus the first 6 bytes) and excluding all its attributes + */ + private int getInternalLength() { + return 2 /* maxStack */ + 2 /* maxLocals */ + 4 /* code length */ + + code.length /* byte-code */ + + 2 /* exception-table length */ + + 8 * (exceptionTable == null ? 0 : exceptionTable.length) /* exception table */ + + 2 /* attributes count */; + } + + /** + * @return LineNumberTable of Code, if it has one + */ + public LineNumberTable getLineNumberTable() { + for (final Attribute attribute : attributes) { + if (attribute instanceof LineNumberTable) { + return (LineNumberTable) attribute; + } + } + return null; + } + + /** + * @return LocalVariableTable of Code, if it has one + */ + public LocalVariableTable getLocalVariableTable() { + for (final Attribute attribute : attributes) { + if (attribute instanceof LocalVariableTable) { + return (LocalVariableTable) attribute; + } + } + return null; + } + + /** + * @return Number of local variables. + */ + public int getMaxLocals() { + return maxLocals; + } + + /** + * @return Maximum size of stack used by this method. + */ + public int getMaxStack() { + return maxStack; + } + + /** + * @param attributes the attributes to set for this Code + */ + public void setAttributes(final Attribute[] attributes) { + this.attributes = attributes != null ? attributes : EMPTY_ARRAY; + super.setLength(calculateLength()); // Adjust length + } + + /** + * @param code byte code + */ + public void setCode(final byte[] code) { + this.code = code != null ? code : ArrayUtils.EMPTY_BYTE_ARRAY; + super.setLength(calculateLength()); // Adjust length + } + + /** + * @param exceptionTable exception table + */ + public void setExceptionTable(final CodeException[] exceptionTable) { + this.exceptionTable = exceptionTable != null ? exceptionTable : CodeException.EMPTY_CODE_EXCEPTION_ARRAY; + super.setLength(calculateLength()); // Adjust length + } + + /** + * @param maxLocals maximum number of local variables + */ + public void setMaxLocals(final int maxLocals) { + this.maxLocals = maxLocals; + } + + /** + * @param maxStack maximum stack size + */ + public void setMaxStack(final int maxStack) { + this.maxStack = maxStack; + } + + /** + * @return String representation of code chunk. + */ + @Override + public String toString() { + return toString(true); + } + + /** + * Converts this object to a String. + * + * @param verbose Provides verbose output when true. + * @return String representation of code chunk. + */ + public String toString(final boolean verbose) { + final StringBuilder buf = new StringBuilder(100); // CHECKSTYLE IGNORE MagicNumber + buf.append("Code(maxStack = ").append(maxStack).append(", maxLocals = ").append(maxLocals).append(", code_length = ").append(code.length).append(")\n") + .append(Utility.codeToString(code, super.getConstantPool(), 0, -1, verbose)); + if (exceptionTable.length > 0) { + buf.append("\nException handler(s) = \n").append("From\tTo\tHandler\tType\n"); + for (final CodeException exception : exceptionTable) { + buf.append(exception.toString(super.getConstantPool(), verbose)).append("\n"); + } + } + if (attributes.length > 0) { + buf.append("\nAttribute(s) = "); + for (final Attribute attribute : attributes) { + buf.append("\n").append(attribute.getName()).append(":"); + buf.append("\n").append(attribute); + } + } + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/CodeException.java b/src/main/java/haidnor/jvm/bcel/classfile/CodeException.java new file mode 100644 index 0000000..2588343 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/CodeException.java @@ -0,0 +1,232 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class represents an entry in the exception table of the Code attribute and is used only there. It + * contains a range in which a particular exception handler is active. + * + *
+ * Code_attribute {
+ *   u2 attribute_name_index;
+ *   u4 attribute_length;
+ *   u2 max_stack;
+ *   u2 max_locals;
+ *   u4 code_length;
+ *   u1 code[code_length];
+ *   u2 exception_table_length;
+ *   {
+ *     u2 start_pc;
+ *     u2 end_pc;
+ *     u2 handler_pc;
+ *     u2 catch_type;
+ *   } exception_table[exception_table_length];
+ *   u2 attributes_count;
+ *   attribute_info attributes[attributes_count];
+ * }
+ * 
+ * + * @see Code + */ +public final class CodeException implements Cloneable, Node { + + /** + * Empty array. + */ + static final CodeException[] EMPTY_CODE_EXCEPTION_ARRAY = {}; + + /** + * Range in the code the exception handler. + */ + private int startPc; + + /** + * active. startPc is inclusive, endPc exclusive. + */ + private int endPc; + + /** + * Starting address of exception handler, i.e., an offset from start of code. + */ + private int handlerPc; + + /* + * If this is zero the handler catches any exception, otherwise it points to the exception class which is to be caught. + */ + private int catchType; + + /** + * Constructs a new instance from another instance. + * + * @param c Source for copying. + */ + public CodeException(final CodeException c) { + this(c.getStartPC(), c.getEndPC(), c.getHandlerPC(), c.getCatchType()); + } + + /** + * Constructs a new instance from a DataInput. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + CodeException(final DataInput file) throws IOException { + this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort()); + } + + /** + * Constructs a new instance. + * + * @param startPc Range in the code the exception handler is active, startPc is inclusive while + * @param endPc is exclusive + * @param handlerPc Starting address of exception handler, i.e., an offset from start of code. + * @param catchType If zero the handler catches any exception, otherwise it points to the exception class which is to be + * caught. + */ + public CodeException(final int startPc, final int endPc, final int handlerPc, final int catchType) { + this.startPc = Args.requireU2(startPc, "startPc"); + this.endPc = Args.requireU2(endPc, "endPc"); + this.handlerPc = Args.requireU2(handlerPc, "handlerPc"); + this.catchType = Args.requireU2(catchType, "catchType"); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitCodeException(this); + } + + /** + * @return deep copy of this object + */ + public CodeException copy() { + try { + return (CodeException) clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + /** + * Dumps code exception to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + public void dump(final DataOutputStream file) throws IOException { + file.writeShort(startPc); + file.writeShort(endPc); + file.writeShort(handlerPc); + file.writeShort(catchType); + } + + /** + * @return 0, if the handler catches any exception, otherwise it points to the exception class which is to be caught. + */ + public int getCatchType() { + return catchType; + } + + /** + * @return Exclusive end index of the region where the handler is active. + */ + public int getEndPC() { + return endPc; + } + + /** + * @return Starting address of exception handler, relative to the code. + */ + public int getHandlerPC() { + return handlerPc; + } + + /** + * @return Inclusive start index of the region where the handler is active. + */ + public int getStartPC() { + return startPc; + } + + /** + * @param catchType the type of exception that is caught + */ + public void setCatchType(final int catchType) { + this.catchType = catchType; + } + + /** + * @param endPc end of handled block + */ + public void setEndPC(final int endPc) { + this.endPc = endPc; + } + + /** + * @param handlerPc where the actual code is + */ + public void setHandlerPC(final int handlerPc) { // TODO unused + this.handlerPc = handlerPc; + } + + /** + * @param startPc start of handled block + */ + public void setStartPC(final int startPc) { // TODO unused + this.startPc = startPc; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + return "CodeException(startPc = " + startPc + ", endPc = " + endPc + ", handlerPc = " + handlerPc + ", catchType = " + catchType + ")"; + } + + public String toString(final ConstantPool cp) { + return toString(cp, true); + } + + /** + * @param cp constant pool source. + * @param verbose Output more if true. + * @return String representation. + */ + public String toString(final ConstantPool cp, final boolean verbose) { + String str; + if (catchType == 0) { + str = "(0)"; + } else { + str = Utility.compactClassName(cp.getConstantString(catchType, Const.CONSTANT_Class), false) + (verbose ? "(" + catchType + ")" : ""); + } + return startPc + "\t" + endPc + "\t" + handlerPc + "\t" + str; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Constant.java b/src/main/java/haidnor/jvm/bcel/classfile/Constant.java new file mode 100644 index 0000000..bb899b5 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Constant.java @@ -0,0 +1,199 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.BCELComparator; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Objects; + +/** + * Abstract superclass for classes to represent the different constant types in the constant pool of a class file. The + * classes keep closely to the JVM specification. + */ +public abstract class Constant implements Cloneable, Node { + + private static BCELComparator bcelComparator = new BCELComparator() { + + @Override + public boolean equals(final Object o1, final Object o2) { + final Constant THIS = (Constant) o1; + final Constant THAT = (Constant) o2; + return Objects.equals(THIS.toString(), THAT.toString()); + } + + @Override + public int hashCode(final Object o) { + final Constant THIS = (Constant) o; + return THIS.toString().hashCode(); + } + }; + + /** + * @return Comparison strategy object + */ + public static BCELComparator getComparator() { + return bcelComparator; + } + + /** + * Reads one constant from the given input, the type depends on a tag byte. + * + * @param dataInput Input stream + * @return Constant object + * @throws IOException if an I/O error occurs reading from the given {@code dataInput}. + * @throws ClassFormatException if the next byte is not recognized + * @since 6.0 made public + */ + public static Constant readConstant(final DataInput dataInput) throws IOException, ClassFormatException { + final byte b = dataInput.readByte(); // Read tag byte + switch (b) { + case Const.CONSTANT_Class: + return new ConstantClass(dataInput); + case Const.CONSTANT_Fieldref: + return new ConstantFieldref(dataInput); + case Const.CONSTANT_Methodref: + return new ConstantMethodref(dataInput); + case Const.CONSTANT_InterfaceMethodref: + return new ConstantInterfaceMethodref(dataInput); + case Const.CONSTANT_String: + return new ConstantString(dataInput); + case Const.CONSTANT_Integer: + return new ConstantInteger(dataInput); + case Const.CONSTANT_Float: + return new ConstantFloat(dataInput); + case Const.CONSTANT_Long: + return new ConstantLong(dataInput); + case Const.CONSTANT_Double: + return new ConstantDouble(dataInput); + case Const.CONSTANT_NameAndType: + return new ConstantNameAndType(dataInput); + case Const.CONSTANT_Utf8: + return ConstantUtf8.getInstance(dataInput); + case Const.CONSTANT_MethodHandle: + return new ConstantMethodHandle(dataInput); + case Const.CONSTANT_MethodType: + return new ConstantMethodType(dataInput); + case Const.CONSTANT_Dynamic: + return new ConstantDynamic(dataInput); + case Const.CONSTANT_InvokeDynamic: + return new ConstantInvokeDynamic(dataInput); + case Const.CONSTANT_Module: + return new ConstantModule(dataInput); + case Const.CONSTANT_Package: + return new ConstantPackage(dataInput); + default: + throw new ClassFormatException("Invalid byte tag in constant pool: " + b); + } + } + + /** + * @param comparator Comparison strategy object + */ + public static void setComparator(final BCELComparator comparator) { + bcelComparator = comparator; + } + + /* + * In fact this tag is redundant since we can distinguish different 'Constant' objects by their type, i.e., via + * 'instanceof'. In some places we will use the tag for switch()es anyway. + * + * First, we want match the specification as closely as possible. Second we need the tag as an index to select the + * corresponding class name from the 'CONSTANT_NAMES' array. + */ + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected byte tag; // TODO should be private & final + + Constant(final byte tag) { + this.tag = tag; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public abstract void accept(Visitor v); + + @Override + public Object clone() { + try { + return super.clone(); + } catch (final CloneNotSupportedException e) { + throw new Error("Clone Not Supported"); // never happens + } + } + + /** + * @return deep copy of this constant + */ + public Constant copy() { + try { + return (Constant) super.clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + public abstract void dump(DataOutputStream file) throws IOException; + + /** + * Returns value as defined by given BCELComparator strategy. By default two Constant objects are said to be equal when + * the result of toString() is equal. + * + * @see Object#equals(Object) + */ + @Override + public boolean equals(final Object obj) { + return bcelComparator.equals(this, obj); + } + + /** + * @return Tag of constant, i.e., its type. No setTag() method to avoid confusion. + */ + public final byte getTag() { + return tag; + } + + /** + * Returns value as defined by given BCELComparator strategy. By default return the hashcode of the result of + * toString(). + * + * @see Object#hashCode() + */ + @Override + public int hashCode() { + return bcelComparator.hashCode(this); + } + + /** + * @return String representation. + */ + @Override + public String toString() { + return Const.getConstantName(tag) + "[" + tag + "]"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantCP.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantCP.java new file mode 100644 index 0000000..8f6a266 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantCP.java @@ -0,0 +1,140 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Abstract super class for Fieldref, Methodref, InterfaceMethodref and InvokeDynamic constants. + * + * @see ConstantFieldref + * @see ConstantMethodref + * @see ConstantInterfaceMethodref + * @see ConstantInvokeDynamic + */ +public abstract class ConstantCP extends Constant { + + /** + * References to the constants containing the class and the field signature + */ + // Note that this field is used to store the + // bootstrap_method_attr_index of a ConstantInvokeDynamic. + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected int class_index; // TODO make private (has getter & setter) + // This field has the same meaning for all subclasses. + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected int name_and_type_index; // TODO make private (has getter & setter) + + /** + * Initialize instance from file data. + * + * @param tag Constant type tag + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantCP(final byte tag, final DataInput file) throws IOException { + this(tag, file.readUnsignedShort(), file.readUnsignedShort()); + } + + /** + * @param classIndex Reference to the class containing the field + * @param nameAndTypeIndex and the field signature + */ + protected ConstantCP(final byte tag, final int classIndex, final int nameAndTypeIndex) { + super(tag); + this.class_index = classIndex; + this.name_and_type_index = nameAndTypeIndex; + } + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantCP(final ConstantCP c) { + this(c.getTag(), c.getClassIndex(), c.getNameAndTypeIndex()); + } + + /** + * Dump constant field reference to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public final void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeShort(class_index); + file.writeShort(name_and_type_index); + } + + /** + * @return Class this field belongs to. + */ + public String getClass(final ConstantPool cp) { + return cp.constantToString(class_index, Const.CONSTANT_Class); + } + + /** + * @return Reference (index) to class this constant refers to. + */ + public final int getClassIndex() { + return class_index; + } + + /** + * @return Reference (index) to signature of the field. + */ + public final int getNameAndTypeIndex() { + return name_and_type_index; + } + + /** + * @param classIndex points to Constant_class + */ + public final void setClassIndex(final int classIndex) { + this.class_index = classIndex; + } + + /** + * @param nameAndTypeIndex points to Constant_NameAndType + */ + public final void setNameAndTypeIndex(final int nameAndTypeIndex) { + this.name_and_type_index = nameAndTypeIndex; + } + + /** + * @return String representation. + * + * not final as ConstantInvokeDynamic needs to modify + */ + @Override + public String toString() { + return super.toString() + "(class_index = " + class_index + ", name_and_type_index = " + name_and_type_index + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantClass.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantClass.java new file mode 100644 index 0000000..59179da --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantClass.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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to a (external) class. + * + * @see Constant + */ +public final class ConstantClass extends Constant implements ConstantObject { + + private int nameIndex; // Identical to ConstantString except for the name + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantClass(final ConstantClass c) { + this(c.getNameIndex()); + } + + /** + * Constructs an instance from file data. + * + * @param dataInput Input stream + * @throws IOException if an I/O error occurs reading from the given {@code dataInput}. + */ + ConstantClass(final DataInput dataInput) throws IOException { + this(dataInput.readUnsignedShort()); + } + + /** + * @param nameIndex Name index in constant pool. Should refer to a ConstantUtf8. + */ + public ConstantClass(final int nameIndex) { + super(Const.CONSTANT_Class); + this.nameIndex = nameIndex; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantClass(this); + } + + /** + * Dumps constant class to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs writing to the DataOutputStream. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeShort(nameIndex); + } + + /** + * @return dereferenced string + */ + public String getBytes(final ConstantPool cp) { + return (String) getConstantValue(cp); + } + + /** + * @return String object + */ + @Override + public Object getConstantValue(final ConstantPool cp) { + return cp.getConstantUtf8(nameIndex).getBytes(); + } + + /** + * @return Name index in constant pool of class name. + */ + public int getNameIndex() { + return nameIndex; + } + + /** + * @param nameIndex the name index in the constant pool of this Constant Class + */ + public void setNameIndex(final int nameIndex) { + this.nameIndex = nameIndex; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + return super.toString() + "(nameIndex = " + nameIndex + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantDouble.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantDouble.java new file mode 100644 index 0000000..745f9c3 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantDouble.java @@ -0,0 +1,113 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to a Double object. + * + * @see Constant + */ +public final class ConstantDouble extends Constant implements ConstantObject { + + private double bytes; + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantDouble(final ConstantDouble c) { + this(c.getBytes()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantDouble(final DataInput file) throws IOException { + this(file.readDouble()); + } + + /** + * @param bytes Data + */ + public ConstantDouble(final double bytes) { + super(Const.CONSTANT_Double); + this.bytes = bytes; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantDouble(this); + } + + /** + * Dump constant double to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeDouble(bytes); + } + + /** + * @return data, i.e., 8 bytes. + */ + public double getBytes() { + return bytes; + } + + /** + * @return Double object + */ + @Override + public Object getConstantValue(final ConstantPool cp) { + return Double.valueOf(bytes); + } + + /** + * @param bytes the raw bytes that represent the double value + */ + public void setBytes(final double bytes) { + this.bytes = bytes; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + return super.toString() + "(bytes = " + bytes + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantDynamic.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantDynamic.java new file mode 100644 index 0000000..bea3381 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantDynamic.java @@ -0,0 +1,86 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to a dynamically computed + * constant. + * + * @see Constant + * @see Change request for JEP + * 309 + * @since 6.3 + */ +public final class ConstantDynamic extends ConstantCP { + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantDynamic(final ConstantDynamic c) { + this(c.getBootstrapMethodAttrIndex(), c.getNameAndTypeIndex()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantDynamic(final DataInput file) throws IOException { + this(file.readShort(), file.readShort()); + } + + public ConstantDynamic(final int bootstrapMethodAttrIndex, final int nameAndTypeIndex) { + super(Const.CONSTANT_Dynamic, bootstrapMethodAttrIndex, nameAndTypeIndex); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e., + * the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantDynamic(this); + } + + /** + * @return Reference (index) to bootstrap method this constant refers to. + * + * Note that this method is a functional duplicate of getClassIndex for use by ConstantInvokeDynamic. + * @since 6.0 + */ + public int getBootstrapMethodAttrIndex() { + return super.getClassIndex(); // AKA bootstrap_method_attr_index + } + + /** + * @return String representation + */ + @Override + public String toString() { + return super.toString().replace("class_index", "bootstrap_method_attr_index"); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantFieldref.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantFieldref.java new file mode 100644 index 0000000..dfaffc2 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantFieldref.java @@ -0,0 +1,66 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.IOException; + +/** + * This class represents a constant pool reference to a field. + */ +public final class ConstantFieldref extends ConstantCP { + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantFieldref(final ConstantFieldref c) { + super(Const.CONSTANT_Fieldref, c.getClassIndex(), c.getNameAndTypeIndex()); + } + + /** + * Initialize instance from input data. + * + * @param input input stream + * @throws IOException if an I/O error occurs. + */ + ConstantFieldref(final DataInput input) throws IOException { + super(Const.CONSTANT_Fieldref, input); + } + + /** + * @param classIndex Reference to the class containing the Field + * @param nameAndTypeIndex and the Field signature + */ + public ConstantFieldref(final int classIndex, final int nameAndTypeIndex) { + super(Const.CONSTANT_Fieldref, classIndex, nameAndTypeIndex); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of Fields, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantFieldref(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantFloat.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantFloat.java new file mode 100644 index 0000000..5b2ece3 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantFloat.java @@ -0,0 +1,114 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to a float object. + * + * @see Constant + */ +public final class ConstantFloat extends Constant implements ConstantObject { + + private float bytes; + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a + * physical copy. + * + * @param c Source to copy. + */ + public ConstantFloat(final ConstantFloat c) { + this(c.getBytes()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantFloat(final DataInput file) throws IOException { + this(file.readFloat()); + } + + /** + * @param bytes Data + */ + public ConstantFloat(final float bytes) { + super(Const.CONSTANT_Float); + this.bytes = bytes; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantFloat(this); + } + + /** + * Dump constant float to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeFloat(bytes); + } + + /** + * @return data, i.e., 4 bytes. + */ + public float getBytes() { + return bytes; + } + + /** + * @return Float object + */ + @Override + public Object getConstantValue(final ConstantPool cp) { + return Float.valueOf(bytes); + } + + /** + * @param bytes the raw bytes that represent this float + */ + public void setBytes(final float bytes) { + this.bytes = bytes; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + return super.toString() + "(bytes = " + bytes + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantInteger.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantInteger.java new file mode 100644 index 0000000..2540969 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantInteger.java @@ -0,0 +1,113 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to an int object. + * + * @see Constant + */ +public final class ConstantInteger extends Constant implements ConstantObject { + + private int bytes; + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantInteger(final ConstantInteger c) { + this(c.getBytes()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantInteger(final DataInput file) throws IOException { + this(file.readInt()); + } + + /** + * @param bytes Data + */ + public ConstantInteger(final int bytes) { + super(Const.CONSTANT_Integer); + this.bytes = bytes; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantInteger(this); + } + + /** + * Dump constant integer to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeInt(bytes); + } + + /** + * @return data, i.e., 4 bytes. + */ + public int getBytes() { + return bytes; + } + + /** + * @return Integer object + */ + @Override + public Object getConstantValue(final ConstantPool cp) { + return Integer.valueOf(bytes); + } + + /** + * @param bytes the raw bytes that represent this integer + */ + public void setBytes(final int bytes) { + this.bytes = bytes; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + return super.toString() + "(bytes = " + bytes + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantInterfaceMethodref.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantInterfaceMethodref.java new file mode 100644 index 0000000..28521c3 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantInterfaceMethodref.java @@ -0,0 +1,66 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.IOException; + +/** + * This class represents a constant pool reference to an interface method. + */ +public final class ConstantInterfaceMethodref extends ConstantCP { + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantInterfaceMethodref(final ConstantInterfaceMethodref c) { + super(Const.CONSTANT_InterfaceMethodref, c.getClassIndex(), c.getNameAndTypeIndex()); + } + + /** + * Initialize instance from input data. + * + * @param input input stream + * @throws IOException if an I/O error occurs. + */ + ConstantInterfaceMethodref(final DataInput input) throws IOException { + super(Const.CONSTANT_InterfaceMethodref, input); + } + + /** + * @param classIndex Reference to the class containing the method + * @param nameAndTypeIndex and the method signature + */ + public ConstantInterfaceMethodref(final int classIndex, final int nameAndTypeIndex) { + super(Const.CONSTANT_InterfaceMethodref, classIndex, nameAndTypeIndex); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantInterfaceMethodref(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantInvokeDynamic.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantInvokeDynamic.java new file mode 100644 index 0000000..3cb848a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantInvokeDynamic.java @@ -0,0 +1,85 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to a invoke dynamic. + * + * @see Constant + * @see The + * CONSTANT_InvokeDynamic_info Structure in The Java Virtual Machine Specification + * @since 6.0 + */ +public final class ConstantInvokeDynamic extends ConstantCP { + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantInvokeDynamic(final ConstantInvokeDynamic c) { + this(c.getBootstrapMethodAttrIndex(), c.getNameAndTypeIndex()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantInvokeDynamic(final DataInput file) throws IOException { + this(file.readUnsignedShort(), file.readUnsignedShort()); + } + + public ConstantInvokeDynamic(final int bootstrapMethodAttrIndex, final int nameAndTypeIndex) { + super(Const.CONSTANT_InvokeDynamic, bootstrapMethodAttrIndex, nameAndTypeIndex); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e., + * the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantInvokeDynamic(this); + } + + /** + * @return Reference (index) to bootstrap method this constant refers to. + * + * Note that this method is a functional duplicate of getClassIndex for use by ConstantInvokeDynamic. + * @since 6.0 + */ + public int getBootstrapMethodAttrIndex() { + return super.getClassIndex(); // AKA bootstrap_method_attr_index + } + + /** + * @return String representation + */ + @Override + public String toString() { + return super.toString().replace("class_index", "bootstrap_method_attr_index"); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantLong.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantLong.java new file mode 100644 index 0000000..7c57f20 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantLong.java @@ -0,0 +1,113 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to a long object. + * + * @see Constant + */ +public final class ConstantLong extends Constant implements ConstantObject { + + private long bytes; + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantLong(final ConstantLong c) { + this(c.getBytes()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantLong(final DataInput file) throws IOException { + this(file.readLong()); + } + + /** + * @param bytes Data + */ + public ConstantLong(final long bytes) { + super(Const.CONSTANT_Long); + this.bytes = bytes; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantLong(this); + } + + /** + * Dump constant long to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeLong(bytes); + } + + /** + * @return data, i.e., 8 bytes. + */ + public long getBytes() { + return bytes; + } + + /** + * @return Long object + */ + @Override + public Object getConstantValue(final ConstantPool cp) { + return Long.valueOf(bytes); + } + + /** + * @param bytes the raw bytes that represent this long + */ + public void setBytes(final long bytes) { + this.bytes = bytes; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + return super.toString() + "(bytes = " + bytes + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodHandle.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodHandle.java new file mode 100644 index 0000000..98891fe --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodHandle.java @@ -0,0 +1,108 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to a method handle. + * + * @see Constant + * @since 6.0 + */ +public final class ConstantMethodHandle extends Constant { + + private int referenceKind; + private int referenceIndex; + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantMethodHandle(final ConstantMethodHandle c) { + this(c.getReferenceKind(), c.getReferenceIndex()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantMethodHandle(final DataInput file) throws IOException { + this(file.readUnsignedByte(), file.readUnsignedShort()); + } + + public ConstantMethodHandle(final int referenceKind, final int referenceIndex) { + super(Const.CONSTANT_MethodHandle); + this.referenceKind = referenceKind; + this.referenceIndex = referenceIndex; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e., + * the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantMethodHandle(this); + } + + /** + * Dump method kind and index to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeByte(referenceKind); + file.writeShort(referenceIndex); + } + + public int getReferenceIndex() { + return referenceIndex; + } + + public int getReferenceKind() { + return referenceKind; + } + + public void setReferenceIndex(final int referenceIndex) { + this.referenceIndex = referenceIndex; + } + + public void setReferenceKind(final int referenceKind) { + this.referenceKind = referenceKind; + } + + /** + * @return String representation + */ + @Override + public String toString() { + return super.toString() + "(referenceKind = " + referenceKind + ", referenceIndex = " + referenceIndex + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodType.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodType.java new file mode 100644 index 0000000..c9d11ea --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodType.java @@ -0,0 +1,97 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to a method type. + * + * @see Constant + * @since 6.0 + */ +public final class ConstantMethodType extends Constant { + + private int descriptorIndex; + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantMethodType(final ConstantMethodType c) { + this(c.getDescriptorIndex()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantMethodType(final DataInput file) throws IOException { + this(file.readUnsignedShort()); + } + + public ConstantMethodType(final int descriptorIndex) { + super(Const.CONSTANT_MethodType); + this.descriptorIndex = descriptorIndex; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e., + * the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantMethodType(this); + } + + /** + * Dump name and signature index to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeShort(descriptorIndex); + } + + public int getDescriptorIndex() { + return descriptorIndex; + } + + public void setDescriptorIndex(final int descriptorIndex) { + this.descriptorIndex = descriptorIndex; + } + + /** + * @return String representation + */ + @Override + public String toString() { + return super.toString() + "(descriptorIndex = " + descriptorIndex + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodref.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodref.java new file mode 100644 index 0000000..e9b02b3 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantMethodref.java @@ -0,0 +1,66 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.IOException; + +/** + * This class represents a constant pool reference to a method. + */ +public final class ConstantMethodref extends ConstantCP { + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantMethodref(final ConstantMethodref c) { + super(Const.CONSTANT_Methodref, c.getClassIndex(), c.getNameAndTypeIndex()); + } + + /** + * Initialize instance from input data. + * + * @param input input stream + * @throws IOException if an I/O error occurs. + */ + ConstantMethodref(final DataInput input) throws IOException { + super(Const.CONSTANT_Methodref, input); + } + + /** + * @param classIndex Reference to the class containing the method + * @param nameAndTypeIndex and the method signature + */ + public ConstantMethodref(final int classIndex, final int nameAndTypeIndex) { + super(Const.CONSTANT_Methodref, classIndex, nameAndTypeIndex); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantMethodref(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantModule.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantModule.java new file mode 100644 index 0000000..5552e5d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantModule.java @@ -0,0 +1,125 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to a module. + * + *

+ * Note: Early access Java 9 support- currently subject to change + *

+ * + * @see Constant + * @since 6.1 + */ +public final class ConstantModule extends Constant implements ConstantObject { + + private int nameIndex; + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantModule(final ConstantModule c) { + this(c.getNameIndex()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantModule(final DataInput file) throws IOException { + this(file.readUnsignedShort()); + } + + /** + * @param nameIndex Name index in constant pool. Should refer to a ConstantUtf8. + */ + public ConstantModule(final int nameIndex) { + super(Const.CONSTANT_Module); + this.nameIndex = nameIndex; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e., + * the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantModule(this); + } + + /** + * Dump constant module to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeShort(nameIndex); + } + + /** + * @return dereferenced string + */ + public String getBytes(final ConstantPool cp) { + return (String) getConstantValue(cp); + } + + /** + * @return String object + */ + @Override + public Object getConstantValue(final ConstantPool cp) { + return cp.getConstantUtf8(nameIndex).getBytes(); + } + + /** + * @return Name index in constant pool of module name. + */ + public int getNameIndex() { + return nameIndex; + } + + /** + * @param nameIndex the name index in the constant pool of this Constant Module + */ + public void setNameIndex(final int nameIndex) { + this.nameIndex = nameIndex; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + return super.toString() + "(nameIndex = " + nameIndex + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantNameAndType.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantNameAndType.java new file mode 100644 index 0000000..547e4cd --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantNameAndType.java @@ -0,0 +1,138 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to the name and signature of a + * field or method. + * + * @see Constant + */ +public final class ConstantNameAndType extends Constant { + + private int nameIndex; // Name of field/method + private int signatureIndex; // and its signature. + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantNameAndType(final ConstantNameAndType c) { + this(c.getNameIndex(), c.getSignatureIndex()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantNameAndType(final DataInput file) throws IOException { + this(file.readUnsignedShort(), file.readUnsignedShort()); + } + + /** + * @param nameIndex Name of field/method + * @param signatureIndex and its signature + */ + public ConstantNameAndType(final int nameIndex, final int signatureIndex) { + super(Const.CONSTANT_NameAndType); + this.nameIndex = nameIndex; + this.signatureIndex = signatureIndex; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantNameAndType(this); + } + + /** + * Dump name and signature index to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeShort(nameIndex); + file.writeShort(signatureIndex); + } + + /** + * @return name + */ + public String getName(final ConstantPool cp) { + return cp.constantToString(getNameIndex(), Const.CONSTANT_Utf8); + } + + /** + * @return Name index in constant pool of field/method name. + */ + public int getNameIndex() { + return nameIndex; + } + + /** + * @return signature + */ + public String getSignature(final ConstantPool cp) { + return cp.constantToString(getSignatureIndex(), Const.CONSTANT_Utf8); + } + + /** + * @return Index in constant pool of field/method signature. + */ + public int getSignatureIndex() { + return signatureIndex; + } + + /** + * @param nameIndex the name index of this constant + */ + public void setNameIndex(final int nameIndex) { + this.nameIndex = nameIndex; + } + + /** + * @param signatureIndex the signature index in the constant pool of this type + */ + public void setSignatureIndex(final int signatureIndex) { + this.signatureIndex = signatureIndex; + } + + /** + * @return String representation + */ + @Override + public String toString() { + return super.toString() + "(nameIndex = " + nameIndex + ", signatureIndex = " + signatureIndex + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantObject.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantObject.java new file mode 100644 index 0000000..cf9d18c --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantObject.java @@ -0,0 +1,30 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +/** + * This interface denotes those constants that have a "natural" value, such as ConstantLong, ConstantString, etc.. + * + * @see Constant + */ +public interface ConstantObject { + + /** + * @return object representing the constant, e.g., Long for ConstantLong + */ + Object getConstantValue(ConstantPool cp); +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantPackage.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantPackage.java new file mode 100644 index 0000000..c1376e1 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantPackage.java @@ -0,0 +1,125 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to a package. + * + *

+ * Note: Early access Java 9 support- currently subject to change + *

+ * + * @see Constant + * @since 6.1 + */ +public final class ConstantPackage extends Constant implements ConstantObject { + + private int nameIndex; + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantPackage(final ConstantPackage c) { + this(c.getNameIndex()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantPackage(final DataInput file) throws IOException { + this(file.readUnsignedShort()); + } + + /** + * @param nameIndex Name index in constant pool. Should refer to a ConstantUtf8. + */ + public ConstantPackage(final int nameIndex) { + super(Const.CONSTANT_Package); + this.nameIndex = nameIndex; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e., + * the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantPackage(this); + } + + /** + * Dump constant package to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeShort(nameIndex); + } + + /** + * @return dereferenced string + */ + public String getBytes(final ConstantPool cp) { + return (String) getConstantValue(cp); + } + + /** + * @return String object + */ + @Override + public Object getConstantValue(final ConstantPool cp) { + return cp.getConstantUtf8(nameIndex).getBytes(); + } + + /** + * @return Name index in constant pool of package name. + */ + public int getNameIndex() { + return nameIndex; + } + + /** + * @param nameIndex the name index in the constant pool of this Constant Package + */ + public void setNameIndex(final int nameIndex) { + this.nameIndex = nameIndex; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + return super.toString() + "(nameIndex = " + nameIndex + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantPool.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantPool.java new file mode 100644 index 0000000..29a8082 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantPool.java @@ -0,0 +1,433 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.generic.ConstantPoolGen; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; + +/** + * This class represents the constant pool, i.e., a table of constants, of a parsed classfile. It may contain null references, due to the JVM specification that + * skips an entry after an 8-byte constant (double, long) entry. Those interested in generating constant pools programmatically should see + * ConstantPoolGen. + * + * @see Constant + * @see ConstantPoolGen + */ +public class ConstantPool implements Cloneable, Node, Iterable { + + private static String escape(final String str) { + final int len = str.length(); + final StringBuilder buf = new StringBuilder(len + 5); + final char[] ch = str.toCharArray(); + for (int i = 0; i < len; i++) { + switch (ch[i]) { + case '\n': + buf.append("\\n"); + break; + case '\r': + buf.append("\\r"); + break; + case '\t': + buf.append("\\t"); + break; + case '\b': + buf.append("\\b"); + break; + case '"': + buf.append("\\\""); + break; + default: + buf.append(ch[i]); + } + } + return buf.toString(); + } + + private Constant[] constantPool; + + /** + * @param constantPool Array of constants + */ + public ConstantPool(final Constant[] constantPool) { + this.constantPool = constantPool; + } + + /** + * Reads constants from given input stream. + * + * @param input Input stream + * @throws IOException if problem in readUnsignedShort or readConstant + */ + public ConstantPool(final DataInput input) throws IOException { + byte tag; + final int constantPoolCount = input.readUnsignedShort(); + constantPool = new Constant[constantPoolCount]; + /* + * constantPool[0] is unused by the compiler and may be used freely by the implementation. + */ + for (int i = 1; i < constantPoolCount; i++) { + constantPool[i] = Constant.readConstant(input); + /* + * Quote from the JVM specification: "All eight byte constants take up two spots in the constant pool. If this is the n'th byte in the constant + * pool, then the next item will be numbered n+2" + * + * Thus we have to increment the index counter. + */ + tag = constantPool[i].getTag(); + if (tag == Const.CONSTANT_Double || tag == Const.CONSTANT_Long) { + i++; + } + } + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e., the hierarchy of methods, fields, + * attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantPool(this); + } + + /** + * Resolves constant to a string representation. + * + * @param c Constant to be printed + * @return String representation + * @throws IllegalArgumentException if c is unknown constant type + */ + public String constantToString(Constant c) throws IllegalArgumentException { + String str; + int i; + final byte tag = c.getTag(); + switch (tag) { + case Const.CONSTANT_Class: + i = ((ConstantClass) c).getNameIndex(); + c = getConstantUtf8(i); + str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); + break; + case Const.CONSTANT_String: + i = ((ConstantString) c).getStringIndex(); + c = getConstantUtf8(i); + str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\""; + break; + case Const.CONSTANT_Utf8: + str = ((ConstantUtf8) c).getBytes(); + break; + case Const.CONSTANT_Double: + str = String.valueOf(((ConstantDouble) c).getBytes()); + break; + case Const.CONSTANT_Float: + str = String.valueOf(((ConstantFloat) c).getBytes()); + break; + case Const.CONSTANT_Long: + str = String.valueOf(((ConstantLong) c).getBytes()); + break; + case Const.CONSTANT_Integer: + str = String.valueOf(((ConstantInteger) c).getBytes()); + break; + case Const.CONSTANT_NameAndType: + str = constantToString(((ConstantNameAndType) c).getNameIndex(), Const.CONSTANT_Utf8) + " " + + constantToString(((ConstantNameAndType) c).getSignatureIndex(), Const.CONSTANT_Utf8); + break; + case Const.CONSTANT_InterfaceMethodref: + case Const.CONSTANT_Methodref: + case Const.CONSTANT_Fieldref: + str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class) + "." + + constantToString(((ConstantCP) c).getNameAndTypeIndex(), Const.CONSTANT_NameAndType); + break; + case Const.CONSTANT_MethodHandle: + // Note that the ReferenceIndex may point to a Fieldref, Methodref or + // InterfaceMethodref - so we need to peek ahead to get the actual type. + final ConstantMethodHandle cmh = (ConstantMethodHandle) c; + str = Const.getMethodHandleName(cmh.getReferenceKind()) + " " + + constantToString(cmh.getReferenceIndex(), getConstant(cmh.getReferenceIndex()).getTag()); + break; + case Const.CONSTANT_MethodType: + final ConstantMethodType cmt = (ConstantMethodType) c; + str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8); + break; + case Const.CONSTANT_InvokeDynamic: + final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c; + str = cid.getBootstrapMethodAttrIndex() + ":" + constantToString(cid.getNameAndTypeIndex(), Const.CONSTANT_NameAndType); + break; + case Const.CONSTANT_Dynamic: + final ConstantDynamic cd = (ConstantDynamic) c; + str = cd.getBootstrapMethodAttrIndex() + ":" + constantToString(cd.getNameAndTypeIndex(), Const.CONSTANT_NameAndType); + break; + case Const.CONSTANT_Module: + i = ((ConstantModule) c).getNameIndex(); + c = getConstantUtf8(i); + str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); + break; + case Const.CONSTANT_Package: + i = ((ConstantPackage) c).getNameIndex(); + c = getConstantUtf8(i); + str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); + break; + default: // Never reached + throw new IllegalArgumentException("Unknown constant type " + tag); + } + return str; + } + + /** + * Retrieves constant at 'index' from constant pool and resolve it to a string representation. + * + * @param index of constant in constant pool + * @param tag expected type + * @return String representation + */ + public String constantToString(final int index, final byte tag) { + return constantToString(getConstant(index, tag)); + } + + /** + * @return deep copy of this constant pool + */ + public ConstantPool copy() { + ConstantPool c = null; + try { + c = (ConstantPool) clone(); + c.constantPool = new Constant[constantPool.length]; + for (int i = 1; i < constantPool.length; i++) { + if (constantPool[i] != null) { + c.constantPool[i] = constantPool[i].copy(); + } + } + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return c; + } + + /** + * Dump constant pool to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if problem in writeShort or dump + */ + public void dump(final DataOutputStream file) throws IOException { + /* + * Constants over the size of the constant pool shall not be written out. This is a redundant measure as the ConstantPoolGen should have already + * reported an error back in the situation. + */ + final int size = Math.min(constantPool.length, Const.MAX_CP_ENTRIES); + + file.writeShort(size); + for (int i = 1; i < size; i++) { + if (constantPool[i] != null) { + constantPool[i].dump(file); + } + } + } + + /** + * Gets constant from constant pool. + * + * @param index Index in constant pool + * @return Constant value + * @see Constant + * @throws ClassFormatException if index is invalid + */ + @SuppressWarnings("unchecked") + public T getConstant(final int index) throws ClassFormatException { + return (T) getConstant(index, Constant.class); + } + + /** + * Gets constant from constant pool and check whether it has the expected type. + * + * @param index Index in constant pool + * @param tag Tag of expected constant, i.e., its type + * @return Constant value + * @see Constant + * @throws ClassFormatException if constant type does not match tag + */ + @SuppressWarnings("unchecked") + public T getConstant(final int index, final byte tag) throws ClassFormatException { + return (T) getConstant(index, tag, Constant.class); + } + + /** + * Gets constant from constant pool and check whether it has the expected type. + * + * @param index Index in constant pool + * @param tag Tag of expected constant, i.e., its type + * @return Constant value + * @see Constant + * @throws ClassFormatException if constant type does not match tag + * @since 6.6.0 + */ + public T getConstant(final int index, final byte tag, final Class castTo) throws ClassFormatException { + final T c = getConstant(index); + if (c.getTag() != tag) { + throw new ClassFormatException("Expected class '" + Const.getConstantName(tag) + "' at index " + index + " and got " + c); + } + return c; + } + + /** + * Gets constant from constant pool. + * + * @param A {@link Constant} subclass + * @param index Index in constant pool + * @param castTo The {@link Constant} subclass to cast to. + * @return Constant value + * @see Constant + * @throws ClassFormatException if index is invalid + * @since 6.6.0 + */ + public T getConstant(final int index, final Class castTo) throws ClassFormatException { + if (index >= constantPool.length || index < 0) { + throw new ClassFormatException("Invalid constant pool reference using index: " + index + ". Constant pool size is: " + constantPool.length); + } + if (constantPool[index] != null && !castTo.isAssignableFrom(constantPool[index].getClass())) { + throw new ClassFormatException("Invalid constant pool reference at index: " + index + + ". Expected " + castTo + " but was " + constantPool[index].getClass()); + } + // Previous check ensures this won't throw a ClassCastException + final T c = castTo.cast(constantPool[index]); + if (c == null + // the 0th element is always null + && index != 0) { + final Constant prev = constantPool[index - 1]; + if (prev == null || prev.getTag() != Const.CONSTANT_Double && prev.getTag() != Const.CONSTANT_Long) { + throw new ClassFormatException("Constant pool at index " + index + " is null."); + } + } + return c; + } + + /** + * Gets constant from constant pool and check whether it has the expected type. + * + * @param index Index in constant pool + * @return ConstantInteger value + * @see ConstantInteger + * @throws ClassFormatException if constant type does not match tag + */ + public ConstantInteger getConstantInteger(final int index) { + return getConstant(index, Const.CONSTANT_Integer, ConstantInteger.class); + } + + /** + * @return Array of constants. + * @see Constant + */ + public Constant[] getConstantPool() { + return constantPool; + } + + /** + * Gets string from constant pool and bypass the indirection of 'ConstantClass' and 'ConstantString' objects. I.e. these classes have an index field that + * points to another entry of the constant pool of type 'ConstantUtf8' which contains the real data. + * + * @param index Index in constant pool + * @param tag Tag of expected constant, either ConstantClass or ConstantString + * @return Contents of string reference + * @see ConstantClass + * @see ConstantString + * @throws IllegalArgumentException if tag is invalid + */ + public String getConstantString(final int index, final byte tag) throws IllegalArgumentException { + int i; + /* + * This switch() is not that elegant, since the four classes have the same contents, they just differ in the name of the index field variable. But we + * want to stick to the JVM naming conventions closely though we could have solved these more elegantly by using the same variable name or by + * subclassing. + */ + switch (tag) { + case Const.CONSTANT_Class: + i = getConstant(index, ConstantClass.class).getNameIndex(); + break; + case Const.CONSTANT_String: + i = getConstant(index, ConstantString.class).getStringIndex(); + break; + case Const.CONSTANT_Module: + i = getConstant(index, ConstantModule.class).getNameIndex(); + break; + case Const.CONSTANT_Package: + i = getConstant(index, ConstantPackage.class).getNameIndex(); + break; + case Const.CONSTANT_Utf8: + return getConstantUtf8(index).getBytes(); + default: + throw new IllegalArgumentException("getConstantString called with illegal tag " + tag); + } + // Finally get the string from the constant pool + return getConstantUtf8(i).getBytes(); + } + + /** + * Gets constant from constant pool and check whether it has the expected type. + * + * @param index Index in constant pool + * @return ConstantUtf8 value + * @see ConstantUtf8 + * @throws ClassFormatException if constant type does not match tag + */ + public ConstantUtf8 getConstantUtf8(final int index) throws ClassFormatException { + return getConstant(index, Const.CONSTANT_Utf8, ConstantUtf8.class); + } + + /** + * @return Length of constant pool. + */ + public int getLength() { + return constantPool == null ? 0 : constantPool.length; + } + + @Override + public Iterator iterator() { + return Arrays.stream(constantPool).iterator(); + } + + /** + * @param constant Constant to set + */ + public void setConstant(final int index, final Constant constant) { + constantPool[index] = constant; + } + + /** + * @param constantPool + */ + public void setConstantPool(final Constant[] constantPool) { + this.constantPool = constantPool; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + for (int i = 1; i < constantPool.length; i++) { + buf.append(i).append(")").append(constantPool[i]).append("\n"); + } + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantString.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantString.java new file mode 100644 index 0000000..07adab4 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantString.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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from the abstract {@link Constant} and represents a reference to a String object. + * + * @see Constant + */ +public final class ConstantString extends Constant implements ConstantObject { + + private int stringIndex; // Identical to ConstantClass except for this name + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public ConstantString(final ConstantString c) { + this(c.getStringIndex()); + } + + /** + * Initialize instance from file data. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantString(final DataInput file) throws IOException { + this(file.readUnsignedShort()); + } + + /** + * @param stringIndex Index of Constant_Utf8 in constant pool + */ + public ConstantString(final int stringIndex) { + super(Const.CONSTANT_String); + this.stringIndex = stringIndex; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantString(this); + } + + /** + * Dump constant field reference to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeShort(stringIndex); + } + + /** + * @return dereferenced string + */ + public String getBytes(final ConstantPool cp) { + return (String) getConstantValue(cp); + } + + /** + * @return String object + */ + @Override + public Object getConstantValue(final ConstantPool cp) { + return cp.getConstantUtf8(stringIndex).getBytes(); + } + + /** + * @return Index in constant pool of the string (ConstantUtf8). + */ + public int getStringIndex() { + return stringIndex; + } + + /** + * @param stringIndex the index into the constant of the string value + */ + public void setStringIndex(final int stringIndex) { + this.stringIndex = stringIndex; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + return super.toString() + "(stringIndex = " + stringIndex + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantUtf8.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantUtf8.java new file mode 100644 index 0000000..555a23e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantUtf8.java @@ -0,0 +1,259 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Extends the abstract {@link Constant} to represent a reference to a UTF-8 encoded string. + *

+ * The following system properties govern caching this class performs. + *

+ *
    + *
  • {@value #SYS_PROP_CACHE_MAX_ENTRIES} (since 6.4): The size of the cache, by default 0, meaning caching is + * disabled.
  • + *
  • {@value #SYS_PROP_CACHE_MAX_ENTRY_SIZE} (since 6.0): The maximum size of the values to cache, by default 200, 0 + * disables caching. Values larger than this are not cached.
  • + *
  • {@value #SYS_PROP_STATISTICS} (since 6.0): Prints statistics on the console when the JVM exits.
  • + *
+ *

+ * Here is a sample Maven invocation with caching disabled: + *

+ * + *
+ * mvn test -Dbcel.statistics=true -Dbcel.maxcached.size=0 -Dbcel.maxcached=0
+ * 
+ *

+ * Here is a sample Maven invocation with caching enabled: + *

+ * + *
+ * mvn test -Dbcel.statistics=true -Dbcel.maxcached.size=100000 -Dbcel.maxcached=5000000
+ * 
+ * + * @see Constant + */ +public final class ConstantUtf8 extends Constant { + + private static class Cache { + + private static final boolean BCEL_STATISTICS = Boolean.getBoolean(SYS_PROP_STATISTICS); + private static final int MAX_ENTRIES = Integer.getInteger(SYS_PROP_CACHE_MAX_ENTRIES, 0).intValue(); + private static final int INITIAL_CAPACITY = (int) (MAX_ENTRIES / 0.75); + + private static final HashMap CACHE = new LinkedHashMap(INITIAL_CAPACITY, 0.75f, true) { + + private static final long serialVersionUID = -8506975356158971766L; + + @Override + protected boolean removeEldestEntry(final Map.Entry eldest) { + return size() > MAX_ENTRIES; + } + }; + + // Set the size to 0 or below to skip caching entirely + private static final int MAX_ENTRY_SIZE = Integer.getInteger(SYS_PROP_CACHE_MAX_ENTRY_SIZE, 200).intValue(); + + static boolean isEnabled() { + return Cache.MAX_ENTRIES > 0 && MAX_ENTRY_SIZE > 0; + } + + } + + // TODO these should perhaps be AtomicInt? + private static volatile int considered; + private static volatile int created; + private static volatile int hits; + private static volatile int skipped; + + private static final String SYS_PROP_CACHE_MAX_ENTRIES = "bcel.maxcached"; + private static final String SYS_PROP_CACHE_MAX_ENTRY_SIZE = "bcel.maxcached.size"; + private static final String SYS_PROP_STATISTICS = "bcel.statistics"; + + static { + if (Cache.BCEL_STATISTICS) { + Runtime.getRuntime().addShutdownHook(new Thread(ConstantUtf8::printStats)); + } + } + + /** + * Clears the cache. + * + * @since 6.4.0 + */ + public static synchronized void clearCache() { + Cache.CACHE.clear(); + } + + // for access by test code + static synchronized void clearStats() { + hits = considered = skipped = created = 0; + } + + /** + * Gets a new or cached instance of the given value. + *

+ * See {@link ConstantUtf8} class Javadoc for details. + *

+ * + * @param value the value. + * @return a new or cached instance of the given value. + * @since 6.0 + */ + public static ConstantUtf8 getCachedInstance(final String value) { + if (value.length() > Cache.MAX_ENTRY_SIZE) { + skipped++; + return new ConstantUtf8(value); + } + considered++; + synchronized (ConstantUtf8.class) { // might be better with a specific lock object + ConstantUtf8 result = Cache.CACHE.get(value); + if (result != null) { + hits++; + return result; + } + result = new ConstantUtf8(value); + Cache.CACHE.put(value, result); + return result; + } + } + + /** + * Gets a new or cached instance of the given value. + *

+ * See {@link ConstantUtf8} class Javadoc for details. + *

+ * + * @param dataInput the value. + * @return a new or cached instance of the given value. + * @throws IOException if an I/O error occurs. + * @since 6.0 + */ + public static ConstantUtf8 getInstance(final DataInput dataInput) throws IOException { + return getInstance(dataInput.readUTF()); + } + + /** + * Gets a new or cached instance of the given value. + *

+ * See {@link ConstantUtf8} class Javadoc for details. + *

+ * + * @param value the value. + * @return a new or cached instance of the given value. + * @since 6.0 + */ + public static ConstantUtf8 getInstance(final String value) { + return Cache.isEnabled() ? getCachedInstance(value) : new ConstantUtf8(value); + } + + // for access by test code + static void printStats() { + final String prefix = "[Apache Commons BCEL]"; + System.err.printf("%s Cache hit %,d/%,d, %d skipped.%n", prefix, hits, considered, skipped); + System.err.printf("%s Total of %,d ConstantUtf8 objects created.%n", prefix, created); + System.err.printf("%s Configuration: %s=%,d, %s=%,d.%n", prefix, SYS_PROP_CACHE_MAX_ENTRIES, Cache.MAX_ENTRIES, SYS_PROP_CACHE_MAX_ENTRY_SIZE, + Cache.MAX_ENTRY_SIZE); + } + + private final String value; + + /** + * Initializes from another object. + * + * @param constantUtf8 the value. + */ + public ConstantUtf8(final ConstantUtf8 constantUtf8) { + this(constantUtf8.getBytes()); + } + + /** + * Initializes instance from file data. + * + * @param dataInput Input stream + * @throws IOException if an I/O error occurs. + */ + ConstantUtf8(final DataInput dataInput) throws IOException { + super(Const.CONSTANT_Utf8); + value = dataInput.readUTF(); + created++; + } + + /** + * @param value Data + */ + public ConstantUtf8(final String value) { + super(Const.CONSTANT_Utf8); + this.value = Objects.requireNonNull(value, "value"); + created++; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantUtf8(this); + } + + /** + * Dumps String in Utf8 format to file stream. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(super.getTag()); + file.writeUTF(value); + } + + /** + * @return Data converted to string. + */ + public String getBytes() { + return value; + } + + /** + * @param bytes the raw bytes of this UTF-8 + * @deprecated (since 6.0) + */ + @java.lang.Deprecated + public void setBytes(final String bytes) { + throw new UnsupportedOperationException(); + } + + /** + * @return String representation + */ + @Override + public String toString() { + return super.toString() + "(\"" + Utility.replace(value, "\n", "\\n") + "\")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ConstantValue.java b/src/main/java/haidnor/jvm/bcel/classfile/ConstantValue.java new file mode 100644 index 0000000..b5c2278 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ConstantValue.java @@ -0,0 +1,156 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from Attribute and represents a constant value, i.e., a default value for initializing + * a class field. This class is instantiated by the Attribute.readAttribute() method. + * + *
+ * ConstantValue_attribute {
+ *   u2 attribute_name_index;
+ *   u4 attribute_length;
+ *   u2 constantvalue_index;
+ * }
+ * 
+ * @see Attribute + */ +public final class ConstantValue extends Attribute { + + private int constantValueIndex; + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a + * physical copy. + * + * @param c Source to copy. + */ + public ConstantValue(final ConstantValue c) { + this(c.getNameIndex(), c.getLength(), c.getConstantValueIndex(), c.getConstantPool()); + } + + /** + * Construct object from input stream. + * + * @param nameIndex Name index in constant pool + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + ConstantValue(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, input.readUnsignedShort(), constantPool); + } + + /** + * @param nameIndex Name index in constant pool + * @param length Content length in bytes + * @param constantValueIndex Index in constant pool + * @param constantPool Array of constants + */ + public ConstantValue(final int nameIndex, final int length, final int constantValueIndex, final ConstantPool constantPool) { + super(Const.ATTR_CONSTANT_VALUE, nameIndex, Args.require(length, 2, "ConstantValue attribute length"), constantPool); + this.constantValueIndex = constantValueIndex; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitConstantValue(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final ConstantValue c = (ConstantValue) clone(); + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump constant value attribute to file stream on binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(constantValueIndex); + } + + /** + * @return Index in constant pool of constant value. + */ + public int getConstantValueIndex() { + return constantValueIndex; + } + + /** + * @param constantValueIndex the index info the constant pool of this constant value + */ + public void setConstantValueIndex(final int constantValueIndex) { + this.constantValueIndex = constantValueIndex; + } + + /** + * @return String representation of constant value. + */ + @Override + public String toString() { + Constant c = super.getConstantPool().getConstant(constantValueIndex); + String buf; + int i; + // Print constant to string depending on its type + switch (c.getTag()) { + case Const.CONSTANT_Long: + buf = String.valueOf(((ConstantLong) c).getBytes()); + break; + case Const.CONSTANT_Float: + buf = String.valueOf(((ConstantFloat) c).getBytes()); + break; + case Const.CONSTANT_Double: + buf = String.valueOf(((ConstantDouble) c).getBytes()); + break; + case Const.CONSTANT_Integer: + buf = String.valueOf(((ConstantInteger) c).getBytes()); + break; + case Const.CONSTANT_String: + i = ((ConstantString) c).getStringIndex(); + c = super.getConstantPool().getConstantUtf8(i); + buf = "\"" + Utility.convertString(((ConstantUtf8) c).getBytes()) + "\""; + break; + default: + throw new IllegalStateException("Type of ConstValue invalid: " + c); + } + return buf; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Deprecated.java b/src/main/java/haidnor/jvm/bcel/classfile/Deprecated.java new file mode 100644 index 0000000..fc2024c --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Deprecated.java @@ -0,0 +1,134 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from Attribute and denotes that this is a deprecated method. It is instantiated from + * the Attribute.readAttribute() method. + * + * @see Attribute + */ +public final class Deprecated extends Attribute { + + private byte[] bytes; + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a + * physical copy. + * + * @param c Source to copy. + */ + public Deprecated(final Deprecated c) { + this(c.getNameIndex(), c.getLength(), c.getBytes(), c.getConstantPool()); + } + + /** + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param bytes Attribute contents + * @param constantPool Array of constants + */ + public Deprecated(final int nameIndex, final int length, final byte[] bytes, final ConstantPool constantPool) { + super(Const.ATTR_DEPRECATED, nameIndex, Args.require0(length, "Deprecated attribute length"), constantPool); + this.bytes = bytes; + } + + /** + * Construct object from input stream. + * + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + Deprecated(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (byte[]) null, constantPool); + if (length > 0) { + bytes = new byte[length]; + input.readFully(bytes); + println("Deprecated attribute with length > 0"); + } + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitDeprecated(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final Deprecated c = (Deprecated) clone(); + if (bytes != null) { + c.bytes = bytes.clone(); + } + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump source file attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + if (super.getLength() > 0) { + file.write(bytes, 0, super.getLength()); + } + } + + /** + * @return data bytes. + */ + public byte[] getBytes() { + return bytes; + } + + /** + * @param bytes the raw bytes that represents this byte array + */ + public void setBytes(final byte[] bytes) { + this.bytes = bytes; + } + + /** + * @return attribute name + */ + @Override + public String toString() { + return Const.getAttributeName(Const.ATTR_DEPRECATED) + ": true"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/DescendingVisitor.java b/src/main/java/haidnor/jvm/bcel/classfile/DescendingVisitor.java new file mode 100644 index 0000000..355da57 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/DescendingVisitor.java @@ -0,0 +1,547 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import java.util.Objects; +import java.util.Stack; +import java.util.stream.Stream; + +/** + * Traverses a JavaClass with another Visitor object 'piggy-backed' that is applied to all components of a JavaClass + * object. I.e. this class supplies the traversal strategy, other classes can make use of it. + */ +public class DescendingVisitor implements Visitor { + private final JavaClass clazz; + + private final Visitor visitor; + + private final Stack stack = new Stack<>(); + + /** + * @param clazz Class to traverse + * @param visitor visitor object to apply to all components + */ + public DescendingVisitor(final JavaClass clazz, final Visitor visitor) { + this.clazz = clazz; + this.visitor = visitor; + } + + private void accept(final E[] node) { + Stream.of(node).forEach(e -> e.accept(this)); + } + + /** + * @return current object + */ + public Object current() { + return stack.peek(); + } + + /** + * @return container of current entitity, i.e., predecessor during traversal + */ + public Object predecessor() { + return predecessor(0); + } + + /** + * @param level nesting level, i.e., 0 returns the direct predecessor + * @return container of current entitity, i.e., predecessor during traversal + */ + public Object predecessor(final int level) { + final int size = stack.size(); + if (size < 2 || level < 0) { + return null; + } + return stack.elementAt(size - (level + 2)); // size - 1 == current + } + + /** + * Start traversal. + */ + public void visit() { + clazz.accept(this); + } + + /** + * @since 6.0 + */ + @Override + public void visitAnnotation(final Annotations annotation) { + stack.push(annotation); + annotation.accept(visitor); + accept(annotation.getAnnotationEntries()); + stack.pop(); + } + + /** + * @since 6.0 + */ + @Override + public void visitAnnotationDefault(final AnnotationDefault obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** + * @since 6.0 + */ + @Override + public void visitAnnotationEntry(final AnnotationEntry annotationEntry) { + stack.push(annotationEntry); + annotationEntry.accept(visitor); + stack.pop(); + } + + /** + * @since 6.0 + */ + @Override + public void visitBootstrapMethods(final BootstrapMethods bm) { + stack.push(bm); + bm.accept(visitor); + // BootstrapMethod[] bms = bm.getBootstrapMethods(); + // for (int i = 0; i < bms.length; i++) + // { + // bms[i].accept(this); + // } + stack.pop(); + } + + @Override + public void visitCode(final Code code) { + stack.push(code); + code.accept(visitor); + accept(code.getExceptionTable()); + accept(code.getAttributes()); + stack.pop(); + } + + @Override + public void visitCodeException(final CodeException ce) { + stack.push(ce); + ce.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantClass(final ConstantClass constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantDouble(final ConstantDouble constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + /** @since 6.3 */ + @Override + public void visitConstantDynamic(final ConstantDynamic obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantFieldref(final ConstantFieldref constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantFloat(final ConstantFloat constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantInteger(final ConstantInteger constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantInterfaceMethodref(final ConstantInterfaceMethodref constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + /** + * @since 6.0 + */ + @Override + public void visitConstantInvokeDynamic(final ConstantInvokeDynamic constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantLong(final ConstantLong constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + /** @since 6.0 */ + @Override + public void visitConstantMethodHandle(final ConstantMethodHandle obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantMethodref(final ConstantMethodref constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + /** @since 6.0 */ + @Override + public void visitConstantMethodType(final ConstantMethodType obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** @since 6.1 */ + @Override + public void visitConstantModule(final ConstantModule obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantNameAndType(final ConstantNameAndType constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + /** @since 6.1 */ + @Override + public void visitConstantPackage(final ConstantPackage obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantPool(final ConstantPool cp) { + stack.push(cp); + cp.accept(visitor); + Stream.of(cp.getConstantPool()).filter(Objects::nonNull).forEach(e -> e.accept(this)); + stack.pop(); + } + + @Override + public void visitConstantString(final ConstantString constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantUtf8(final ConstantUtf8 constant) { + stack.push(constant); + constant.accept(visitor); + stack.pop(); + } + + @Override + public void visitConstantValue(final ConstantValue cv) { + stack.push(cv); + cv.accept(visitor); + stack.pop(); + } + + @Override + public void visitDeprecated(final Deprecated attribute) { + stack.push(attribute); + attribute.accept(visitor); + stack.pop(); + } + + /** + * @since 6.0 + */ + @Override + public void visitEnclosingMethod(final EnclosingMethod obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + @Override + public void visitExceptionTable(final ExceptionTable table) { + stack.push(table); + table.accept(visitor); + stack.pop(); + } + + @Override + public void visitField(final JavaField field) { + stack.push(field); + field.accept(visitor); + accept(field.getAttributes()); + stack.pop(); + } + + @Override + public void visitInnerClass(final InnerClass inner) { + stack.push(inner); + inner.accept(visitor); + stack.pop(); + } + + @Override + public void visitInnerClasses(final InnerClasses ic) { + stack.push(ic); + ic.accept(visitor); + accept(ic.getInnerClasses()); + stack.pop(); + } + + @Override + public void visitJavaClass(final JavaClass clazz) { + stack.push(clazz); + clazz.accept(visitor); + accept(clazz.getFields()); + accept(clazz.getMethods()); + accept(clazz.getAttributes()); + clazz.getConstantPool().accept(this); + stack.pop(); + } + + @Override + public void visitLineNumber(final LineNumber number) { + stack.push(number); + number.accept(visitor); + stack.pop(); + } + + @Override + public void visitLineNumberTable(final LineNumberTable table) { + stack.push(table); + table.accept(visitor); + accept(table.getLineNumberTable()); + stack.pop(); + } + + @Override + public void visitLocalVariable(final LocalVariable var) { + stack.push(var); + var.accept(visitor); + stack.pop(); + } + + @Override + public void visitLocalVariableTable(final LocalVariableTable table) { + stack.push(table); + table.accept(visitor); + accept(table.getLocalVariableTable()); + stack.pop(); + } + + /** + * @since 6.0 + */ + @Override + public void visitLocalVariableTypeTable(final LocalVariableTypeTable obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + @Override + public void visitMethod(final JavaMethod method) { + stack.push(method); + method.accept(visitor); + accept(method.getAttributes()); + stack.pop(); + } + + /** + * @since 6.4.0 + */ + @Override + public void visitMethodParameter(final MethodParameter obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** + * @since 6.0 + */ + @Override + public void visitMethodParameters(final MethodParameters obj) { + stack.push(obj); + obj.accept(visitor); + Stream.of(obj.getParameters()).forEach(e -> e.accept(this)); + stack.pop(); + } + + /** @since 6.4.0 */ + @Override + public void visitModule(final Module obj) { + stack.push(obj); + obj.accept(visitor); + accept(obj.getRequiresTable()); + accept(obj.getExportsTable()); + accept(obj.getOpensTable()); + accept(obj.getProvidesTable()); + stack.pop(); + } + + /** @since 6.4.0 */ + @Override + public void visitModuleExports(final ModuleExports obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** @since 6.4.0 */ + @Override + public void visitModuleMainClass(final ModuleMainClass obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** @since 6.4.0 */ + @Override + public void visitModuleOpens(final ModuleOpens obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** @since 6.4.0 */ + @Override + public void visitModulePackages(final ModulePackages obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** @since 6.4.0 */ + @Override + public void visitModuleProvides(final ModuleProvides obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** @since 6.4.0 */ + @Override + public void visitModuleRequires(final ModuleRequires obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** @since 6.4.0 */ + @Override + public void visitNestHost(final NestHost obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** @since 6.4.0 */ + @Override + public void visitNestMembers(final NestMembers obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** + * @since 6.0 + */ + @Override + public void visitParameterAnnotation(final ParameterAnnotations obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + /** @since 6.0 */ + @Override + public void visitParameterAnnotationEntry(final ParameterAnnotationEntry obj) { + stack.push(obj); + obj.accept(visitor); + stack.pop(); + } + + @Override + public void visitSignature(final Signature attribute) { + stack.push(attribute); + attribute.accept(visitor); + stack.pop(); + } + + @Override + public void visitSourceFile(final SourceFile attribute) { + stack.push(attribute); + attribute.accept(visitor); + stack.pop(); + } + + @Override + public void visitStackMap(final StackMap table) { + stack.push(table); + table.accept(visitor); + accept(table.getStackMap()); + stack.pop(); + } + + @Override + public void visitStackMapEntry(final StackMapEntry var) { + stack.push(var); + var.accept(visitor); + stack.pop(); + } + + @Override + public void visitSynthetic(final Synthetic attribute) { + stack.push(attribute); + attribute.accept(visitor); + stack.pop(); + } + + @Override + public void visitUnknown(final Unknown attribute) { + stack.push(attribute); + attribute.accept(visitor); + stack.pop(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ElementValue.java b/src/main/java/haidnor/jvm/bcel/classfile/ElementValue.java new file mode 100644 index 0000000..fe950f5 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ElementValue.java @@ -0,0 +1,174 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * The element_value structure is documented at https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7.16.1 + * + *
+ * element_value {
+ *    u1 tag;
+ *    union {
+ *        u2 const_value_index;
+ *
+ *        {   u2 type_name_index;
+ *            u2 const_name_index;
+ *        } enum_const_value;
+ *
+ *        u2 class_info_index;
+ *
+ *        annotation annotation_value;
+ *
+ *        {   u2            num_values;
+ *            element_value values[num_values];
+ *        } array_value;
+ *    } value;
+ *}
+ *
+ * @since 6.0 + */ +public abstract class ElementValue { + + public static final byte STRING = 's'; + public static final byte ENUM_CONSTANT = 'e'; + public static final byte CLASS = 'c'; + public static final byte ANNOTATION = '@'; + public static final byte ARRAY = '['; + public static final byte PRIMITIVE_INT = 'I'; + public static final byte PRIMITIVE_BYTE = 'B'; + public static final byte PRIMITIVE_CHAR = 'C'; + public static final byte PRIMITIVE_DOUBLE = 'D'; + public static final byte PRIMITIVE_FLOAT = 'F'; + public static final byte PRIMITIVE_LONG = 'J'; + public static final byte PRIMITIVE_SHORT = 'S'; + public static final byte PRIMITIVE_BOOLEAN = 'Z'; + + /** + * Reads an {@code element_value} as an {@code ElementValue}. + * + * @param input Raw data input. + * @param cpool Constant pool. + * @return a new ElementValue. + * @throws IOException if an I/O error occurs. + */ + public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool) throws IOException { + return readElementValue(input, cpool, 0); + } + + /** + * Reads an {@code element_value} as an {@code ElementValue}. + * + * @param input Raw data input. + * @param cpool Constant pool. + * @param arrayNesting level of current array nesting. + * @return a new ElementValue. + * @throws IOException if an I/O error occurs. + * @since 6.7.0 + */ + public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool, int arrayNesting) + throws IOException { + final byte tag = input.readByte(); + switch (tag) { + case PRIMITIVE_BYTE: + case PRIMITIVE_CHAR: + case PRIMITIVE_DOUBLE: + case PRIMITIVE_FLOAT: + case PRIMITIVE_INT: + case PRIMITIVE_LONG: + case PRIMITIVE_SHORT: + case PRIMITIVE_BOOLEAN: + case STRING: + return new SimpleElementValue(tag, input.readUnsignedShort(), cpool); + + case ENUM_CONSTANT: + return new EnumElementValue(ENUM_CONSTANT, input.readUnsignedShort(), input.readUnsignedShort(), cpool); + + case CLASS: + return new ClassElementValue(CLASS, input.readUnsignedShort(), cpool); + + case ANNOTATION: + // TODO isRuntimeVisible + return new AnnotationElementValue(ANNOTATION, AnnotationEntry.read(input, cpool, false), cpool); + + case ARRAY: + arrayNesting++; + if (arrayNesting > Const.MAX_ARRAY_DIMENSIONS) { + // JVM spec 4.4.1 + throw new ClassFormatException(String.format("Arrays are only valid if they represent %,d or fewer dimensions.", Const.MAX_ARRAY_DIMENSIONS)); + } + final int numArrayVals = input.readUnsignedShort(); + final ElementValue[] evalues = new ElementValue[numArrayVals]; + for (int j = 0; j < numArrayVals; j++) { + evalues[j] = ElementValue.readElementValue(input, cpool, arrayNesting); + } + return new ArrayElementValue(ARRAY, evalues, cpool); + + default: + throw new ClassFormatException("Unexpected element value tag in annotation: " + tag); + } + } + + /** + * @deprecated (since 6.0) will be made private and final; do not access directly, use getter + */ + @java.lang.Deprecated + protected int type; // TODO should be final + /** + * @deprecated (since 6.0) will be made private and final; do not access directly, use getter + */ + @java.lang.Deprecated + protected ConstantPool cpool; // TODO should be final + + protected ElementValue(final int type, final ConstantPool cpool) { + this.type = type; + this.cpool = cpool; + } + + public abstract void dump(DataOutputStream dos) throws IOException; + + /** @since 6.0 */ + final ConstantPool getConstantPool() { + return cpool; + } + + public int getElementValueType() { + return type; + } + + /** @since 6.0 */ + final int getType() { + return type; + } + + public abstract String stringifyValue(); + + public String toShortString() { + return stringifyValue(); + } + + @Override + public String toString() { + return stringifyValue(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ElementValuePair.java b/src/main/java/haidnor/jvm/bcel/classfile/ElementValuePair.java new file mode 100644 index 0000000..bc4db30 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ElementValuePair.java @@ -0,0 +1,65 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * An annotation's element value pair. + * + * @since 6.0 + */ +public class ElementValuePair { + + static final ElementValuePair[] EMPTY_ARRAY = {}; + + private final ElementValue elementValue; + + private final ConstantPool constantPool; + + private final int elementNameIndex; + + public ElementValuePair(final int elementNameIndex, final ElementValue elementValue, final ConstantPool constantPool) { + this.elementValue = elementValue; + this.elementNameIndex = elementNameIndex; + this.constantPool = constantPool; + } + + protected void dump(final DataOutputStream dos) throws IOException { + dos.writeShort(elementNameIndex); // u2 name of the element + elementValue.dump(dos); + } + + public int getNameIndex() { + return elementNameIndex; + } + + public String getNameString() { + return constantPool.getConstantUtf8(elementNameIndex).getBytes(); + } + + public final ElementValue getValue() { + return elementValue; + } + + public String toShortString() { + final StringBuilder result = new StringBuilder(); + result.append(getNameString()).append("=").append(getValue().toShortString()); + return result.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/EmptyVisitor.java b/src/main/java/haidnor/jvm/bcel/classfile/EmptyVisitor.java new file mode 100644 index 0000000..2763aed --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/EmptyVisitor.java @@ -0,0 +1,320 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +/** + * Visitor with empty method bodies, can be extended and used in conjunction with the DescendingVisitor class, e.g. By + * courtesy of David Spencer. + * + * @see DescendingVisitor + */ +public class EmptyVisitor implements Visitor { + protected EmptyVisitor() { + } + + /** + * @since 6.0 + */ + @Override + public void visitAnnotation(final Annotations obj) { + } + + /** + * @since 6.0 + */ + @Override + public void visitAnnotationDefault(final AnnotationDefault obj) { + } + + /** + * @since 6.0 + */ + @Override + public void visitAnnotationEntry(final AnnotationEntry obj) { + } + + /** + * @since 6.0 + */ + @Override + public void visitBootstrapMethods(final BootstrapMethods obj) { + } + + @Override + public void visitCode(final Code obj) { + } + + @Override + public void visitCodeException(final CodeException obj) { + } + + @Override + public void visitConstantClass(final ConstantClass obj) { + } + + @Override + public void visitConstantDouble(final ConstantDouble obj) { + } + + /** + * @since 6.3 + */ + @Override + public void visitConstantDynamic(final ConstantDynamic obj) { + } + + @Override + public void visitConstantFieldref(final ConstantFieldref obj) { + } + + @Override + public void visitConstantFloat(final ConstantFloat obj) { + } + + @Override + public void visitConstantInteger(final ConstantInteger obj) { + } + + @Override + public void visitConstantInterfaceMethodref(final ConstantInterfaceMethodref obj) { + } + + @Override + public void visitConstantInvokeDynamic(final ConstantInvokeDynamic obj) { + } + + @Override + public void visitConstantLong(final ConstantLong obj) { + } + + /** + * @since 6.0 + */ + @Override + public void visitConstantMethodHandle(final ConstantMethodHandle constantMethodHandle) { + } + + @Override + public void visitConstantMethodref(final ConstantMethodref obj) { + } + + /** + * @since 6.0 + */ + @Override + public void visitConstantMethodType(final ConstantMethodType obj) { + } + + /** + * @since 6.1 + */ + @Override + public void visitConstantModule(final ConstantModule constantModule) { + } + + @Override + public void visitConstantNameAndType(final ConstantNameAndType obj) { + } + + /** + * @since 6.1 + */ + @Override + public void visitConstantPackage(final ConstantPackage constantPackage) { + } + + @Override + public void visitConstantPool(final ConstantPool obj) { + } + + @Override + public void visitConstantString(final ConstantString obj) { + } + + @Override + public void visitConstantUtf8(final ConstantUtf8 obj) { + } + + @Override + public void visitConstantValue(final ConstantValue obj) { + } + + @Override + public void visitDeprecated(final Deprecated obj) { + } + + /** + * @since 6.0 + */ + @Override + public void visitEnclosingMethod(final EnclosingMethod obj) { + } + + @Override + public void visitExceptionTable(final ExceptionTable obj) { + } + + @Override + public void visitField(final JavaField obj) { + } + + @Override + public void visitInnerClass(final InnerClass obj) { + } + + @Override + public void visitInnerClasses(final InnerClasses obj) { + } + + @Override + public void visitJavaClass(final JavaClass obj) { + } + + @Override + public void visitLineNumber(final LineNumber obj) { + } + + @Override + public void visitLineNumberTable(final LineNumberTable obj) { + } + + @Override + public void visitLocalVariable(final LocalVariable obj) { + } + + @Override + public void visitLocalVariableTable(final LocalVariableTable obj) { + } + + /** + * @since 6.0 + */ + @Override + public void visitLocalVariableTypeTable(final LocalVariableTypeTable obj) { + } + + @Override + public void visitMethod(final JavaMethod obj) { + } + + /** + * @since 6.0 + * @Override public void visitStackMapTable(StackMapTable obj) { } + */ + + /** + * @since 6.0 + * @Override public void visitStackMapTableEntry(StackMapTableEntry obj) { } + */ + + /** + * @since 6.4.0 + */ + @Override + public void visitMethodParameter(final MethodParameter obj) { + } + + /** + * @since 6.0 + */ + @Override + public void visitMethodParameters(final MethodParameters obj) { + } + + /** @since 6.4.0 */ + @Override + public void visitModule(final Module obj) { + } + + /** @since 6.4.0 */ + @Override + public void visitModuleExports(final ModuleExports obj) { + } + + /** @since 6.4.0 */ + @Override + public void visitModuleMainClass(final ModuleMainClass obj) { + } + + /** @since 6.4.0 */ + @Override + public void visitModuleOpens(final ModuleOpens obj) { + } + + /** @since 6.4.0 */ + @Override + public void visitModulePackages(final ModulePackages obj) { + } + + /** @since 6.4.0 */ + @Override + public void visitModuleProvides(final ModuleProvides obj) { + } + + /** @since 6.4.0 */ + @Override + public void visitModuleRequires(final ModuleRequires obj) { + } + + /** @since 6.4.0 */ + @Override + public void visitNestHost(final NestHost obj) { + } + + /** @since 6.4.0 */ + @Override + public void visitNestMembers(final NestMembers obj) { + } + + /** + * @since 6.0 + */ + @Override + public void visitParameterAnnotation(final ParameterAnnotations obj) { + } + + /** + * @since 6.0 + */ + @Override + public void visitParameterAnnotationEntry(final ParameterAnnotationEntry parameterAnnotationEntry) { + } + + @Override + public void visitSignature(final Signature obj) { + } + + @Override + public void visitSourceFile(final SourceFile obj) { + } + + @Override + public void visitStackMap(final StackMap obj) { + } + + @Override + public void visitStackMapEntry(final StackMapEntry obj) { + } + + @Override + public void visitSynthetic(final Synthetic obj) { + } + + @Override + public void visitUnknown(final Unknown obj) { + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/EnclosingMethod.java b/src/main/java/haidnor/jvm/bcel/classfile/EnclosingMethod.java new file mode 100644 index 0000000..c81d27d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/EnclosingMethod.java @@ -0,0 +1,102 @@ +/** + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This attribute exists for local or anonymous classes and ... there can be only one. + * + * @since 6.0 + */ +public class EnclosingMethod extends Attribute { + + // Pointer to the CONSTANT_Class_info structure representing the + // innermost class that encloses the declaration of the current class. + private int classIndex; + + // If the current class is not immediately enclosed by a method or + // constructor, then the value of the method_index item must be zero. + // Otherwise, the value of the method_index item must point to a + // CONSTANT_NameAndType_info structure representing the name and the + // type of a method in the class referenced by the class we point + // to in the class_index. *It is the compiler responsibility* to + // ensure that the method identified by this index is the closest + // lexically enclosing method that includes the local/anonymous class. + private int methodIndex; + + // Ctors - and code to read an attribute in. + EnclosingMethod(final int nameIndex, final int len, final DataInput input, final ConstantPool cpool) throws IOException { + this(nameIndex, len, input.readUnsignedShort(), input.readUnsignedShort(), cpool); + } + + private EnclosingMethod(final int nameIndex, final int len, final int classIndex, final int methodIndex, final ConstantPool cpool) { + super(Const.ATTR_ENCLOSING_METHOD, nameIndex, Args.require(len, 4, "EnclosingMethod attribute length"), cpool); + this.classIndex = Args.requireU2(classIndex, 0, cpool.getLength(), "EnclosingMethod class index"); + this.methodIndex = Args.requireU2(methodIndex, "EnclosingMethod method index"); + } + + @Override + public void accept(final Visitor v) { + v.visitEnclosingMethod(this); + } + + @Override + public Attribute copy(final ConstantPool constantPool) { + return (Attribute) clone(); + } + + @Override + public final void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(classIndex); + file.writeShort(methodIndex); + } + + public final ConstantClass getEnclosingClass() { + return super.getConstantPool().getConstant(classIndex, Const.CONSTANT_Class, ConstantClass.class); + } + + // Accessors + public final int getEnclosingClassIndex() { + return classIndex; + } + + public final ConstantNameAndType getEnclosingMethod() { + if (methodIndex == 0) { + return null; + } + return super.getConstantPool().getConstant(methodIndex, Const.CONSTANT_NameAndType, ConstantNameAndType.class); + } + + public final int getEnclosingMethodIndex() { + return methodIndex; + } + + public final void setEnclosingClassIndex(final int idx) { + classIndex = idx; + } + + public final void setEnclosingMethodIndex(final int idx) { + methodIndex = idx; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/EnumElementValue.java b/src/main/java/haidnor/jvm/bcel/classfile/EnumElementValue.java new file mode 100644 index 0000000..5f34d75 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/EnumElementValue.java @@ -0,0 +1,67 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * @since 6.0 + */ +public class EnumElementValue extends ElementValue { + // For enum types, these two indices point to the type and value + private final int typeIdx; + + private final int valueIdx; + + public EnumElementValue(final int type, final int typeIdx, final int valueIdx, final ConstantPool cpool) { + super(type, cpool); + if (type != ENUM_CONSTANT) { + throw new ClassFormatException("Only element values of type enum can be built with this ctor - type specified: " + type); + } + this.typeIdx = typeIdx; + this.valueIdx = valueIdx; + } + + @Override + public void dump(final DataOutputStream dos) throws IOException { + dos.writeByte(super.getType()); // u1 type of value (ENUM_CONSTANT == 'e') + dos.writeShort(typeIdx); // u2 + dos.writeShort(valueIdx); // u2 + } + + public String getEnumTypeString() { + return super.getConstantPool().getConstantUtf8(typeIdx).getBytes(); + } + + public String getEnumValueString() { + return super.getConstantPool().getConstantUtf8(valueIdx).getBytes(); + } + + public int getTypeIndex() { + return typeIdx; + } + + public int getValueIndex() { + return valueIdx; + } + + @Override + public String stringifyValue() { + return super.getConstantPool().getConstantUtf8(valueIdx).getBytes(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ExceptionTable.java b/src/main/java/haidnor/jvm/bcel/classfile/ExceptionTable.java new file mode 100644 index 0000000..51385bc --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ExceptionTable.java @@ -0,0 +1,176 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; +import org.apache.commons.lang3.ArrayUtils; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * This class represents the table of exceptions that are thrown by a method. This attribute may be used once per + * method. The name of this class is ExceptionTable for historical reasons; The Java Virtual Machine + * Specification, Second Edition defines this attribute using the name Exceptions (which is inconsistent with + * the other classes). + * + *
+ * Exceptions_attribute {
+ *   u2 attribute_name_index;
+ *   u4 attribute_length;
+ *   u2 number_of_exceptions;
+ *   u2 exception_index_table[number_of_exceptions];
+ * }
+ * 
+ * @see Code + */ +public final class ExceptionTable extends Attribute { + + private int[] exceptionIndexTable; // constant pool + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a + * physical copy. + * + * @param c Source to copy. + */ + public ExceptionTable(final ExceptionTable c) { + this(c.getNameIndex(), c.getLength(), c.getExceptionIndexTable(), c.getConstantPool()); + } + + /** + * Construct object from input stream. + * + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + ExceptionTable(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (int[]) null, constantPool); + final int exceptionCount = input.readUnsignedShort(); + exceptionIndexTable = new int[exceptionCount]; + for (int i = 0; i < exceptionCount; i++) { + exceptionIndexTable[i] = input.readUnsignedShort(); + } + } + + /** + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param exceptionIndexTable Table of indices in constant pool + * @param constantPool Array of constants + */ + public ExceptionTable(final int nameIndex, final int length, final int[] exceptionIndexTable, final ConstantPool constantPool) { + super(Const.ATTR_EXCEPTIONS, nameIndex, length, constantPool); + this.exceptionIndexTable = exceptionIndexTable != null ? exceptionIndexTable : ArrayUtils.EMPTY_INT_ARRAY; + Args.requireU2(this.exceptionIndexTable.length, "exceptionIndexTable.length"); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionTable(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final ExceptionTable c = (ExceptionTable) clone(); + if (exceptionIndexTable != null) { + c.exceptionIndexTable = exceptionIndexTable.clone(); + } + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump exceptions attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(exceptionIndexTable.length); + for (final int index : exceptionIndexTable) { + file.writeShort(index); + } + } + + /** + * @return Array of indices into constant pool of thrown exceptions. + */ + public int[] getExceptionIndexTable() { + return exceptionIndexTable; + } + + /** + * @return class names of thrown exceptions + */ + public String[] getExceptionNames() { + final String[] names = new String[exceptionIndexTable.length]; + Arrays.setAll(names, i -> Utility.pathToPackage(super.getConstantPool().getConstantString(exceptionIndexTable[i], Const.CONSTANT_Class))); + return names; + } + + /** + * @return Length of exception table. + */ + public int getNumberOfExceptions() { + return exceptionIndexTable == null ? 0 : exceptionIndexTable.length; + } + + /** + * @param exceptionIndexTable the list of exception indexes Also redefines number_of_exceptions according to table + * length. + */ + public void setExceptionIndexTable(final int[] exceptionIndexTable) { + this.exceptionIndexTable = exceptionIndexTable != null ? exceptionIndexTable : ArrayUtils.EMPTY_INT_ARRAY; + } + + /** + * @return String representation, i.e., a list of thrown exceptions. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + String str; + buf.append("Exceptions: "); + for (int i = 0; i < exceptionIndexTable.length; i++) { + str = super.getConstantPool().getConstantString(exceptionIndexTable[i], Const.CONSTANT_Class); + buf.append(Utility.compactClassName(str, false)); + if (i < exceptionIndexTable.length - 1) { + buf.append(", "); + } + } + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/FieldOrMethod.java b/src/main/java/haidnor/jvm/bcel/classfile/FieldOrMethod.java new file mode 100644 index 0000000..e1da2e5 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/FieldOrMethod.java @@ -0,0 +1,260 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * Abstract super class for fields and methods. + */ +public abstract class FieldOrMethod extends AccessFlags implements Cloneable, Node { + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected int name_index; // Points to field name in constant pool + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected int signature_index; // Points to encoded signature + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected Attribute[] attributes; // Collection of attributes + + /** + * @deprecated (since 6.0) will be removed (not needed) + */ + @java.lang.Deprecated + protected int attributes_count; // No. of attributes + + // @since 6.0 + private AnnotationEntry[] annotationEntries; // annotations defined on the field or method + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @java.lang.Deprecated + protected ConstantPool constant_pool; + + private String signatureAttributeString; + private boolean searchedForSignatureAttribute; + + FieldOrMethod() { + } + + /** + * Construct object from file stream. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + protected FieldOrMethod(final DataInput file, final ConstantPool constantPool) throws IOException { + this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), null, constantPool); + final int attributesCount = file.readUnsignedShort(); + attributes = new Attribute[attributesCount]; + for (int i = 0; i < attributesCount; i++) { + attributes[i] = Attribute.readAttribute(file, constantPool); + } + this.attributes_count = attributesCount; // init deprecated field + } + + /** + * Construct object from file stream. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + * @deprecated (6.0) Use {@link #FieldOrMethod(DataInput, ConstantPool)} instead. + */ + @java.lang.Deprecated + protected FieldOrMethod(final DataInputStream file, final ConstantPool constantPool) throws IOException { + this((DataInput) file, constantPool); + } + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a + * physical copy. + * + * @param c Source to copy. + */ + protected FieldOrMethod(final FieldOrMethod c) { + this(c.getAccessFlags(), c.getNameIndex(), c.getSignatureIndex(), c.getAttributes(), c.getConstantPool()); + } + + /** + * @param accessFlags Access rights of method + * @param nameIndex Points to field name in constant pool + * @param signatureIndex Points to encoded signature + * @param attributes Collection of attributes + * @param constantPool Array of constants + */ + protected FieldOrMethod(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes, + final ConstantPool constantPool) { + super(accessFlags); + this.name_index = nameIndex; + this.signature_index = signatureIndex; + this.constant_pool = constantPool; + setAttributes(attributes); + } + + /** + * @return deep copy of this field + */ + protected FieldOrMethod copy_(final ConstantPool constantPool) { + try { + final FieldOrMethod c = (FieldOrMethod) clone(); + c.constant_pool = constantPool; + c.attributes = new Attribute[attributes.length]; + c.attributes_count = attributes_count; // init deprecated field + Arrays.setAll(c.attributes, i -> attributes[i].copy(constantPool)); + return c; + } catch (final CloneNotSupportedException e) { + throw new IllegalStateException(e); + } + } + + /** + * Dump object to file stream on binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + public final void dump(final DataOutputStream file) throws IOException { + file.writeShort(super.getAccessFlags()); + file.writeShort(name_index); + file.writeShort(signature_index); + file.writeShort(attributes_count); + if (attributes != null) { + for (final Attribute attribute : attributes) { + attribute.dump(file); + } + } + } + + /** + * @return Annotations on the field or method + * @since 6.0 + */ + public AnnotationEntry[] getAnnotationEntries() { + if (annotationEntries == null) { + annotationEntries = AnnotationEntry.createAnnotationEntries(getAttributes()); + } + + return annotationEntries; + } + + /** + * @return Collection of object attributes. + */ + public final Attribute[] getAttributes() { + return attributes; + } + + /** + * @return Constant pool used by this object. + */ + public final ConstantPool getConstantPool() { + return constant_pool; + } + + /** + * Hunts for a signature attribute on the member and returns its contents. So where the 'regular' signature may be + * (Ljava/util/Vector;)V the signature attribute may in fact say 'Ljava/lang/Vector<Ljava/lang/String>;' Coded for + * performance - searches for the attribute only when requested - only searches for it once. + * + * @since 6.0 + */ + public final String getGenericSignature() { + if (!searchedForSignatureAttribute) { + boolean found = false; + for (int i = 0; !found && i < attributes.length; i++) { + if (attributes[i] instanceof Signature) { + signatureAttributeString = ((Signature) attributes[i]).getSignature(); + found = true; + } + } + searchedForSignatureAttribute = true; + } + return signatureAttributeString; + } + + /** + * @return Name of object, i.e., method name or field name + */ + public final String getName() { + return constant_pool.getConstantUtf8(name_index).getBytes(); + } + + /** + * @return Index in constant pool of object's name. + */ + public final int getNameIndex() { + return name_index; + } + + /** + * @return String representation of object's type signature (java style) + */ + public final String getSignature() { + return constant_pool.getConstantUtf8(signature_index).getBytes(); + } + + /** + * @return Index in constant pool of field signature. + */ + public final int getSignatureIndex() { + return signature_index; + } + + /** + * @param attributes Collection of object attributes. + */ + public final void setAttributes(final Attribute[] attributes) { + this.attributes = attributes; + this.attributes_count = attributes != null ? attributes.length : 0; // init deprecated field + } + + /** + * @param constantPool Constant pool to be used for this object. + */ + public final void setConstantPool(final ConstantPool constantPool) { + this.constant_pool = constantPool; + } + + /** + * @param nameIndex Index in constant pool of object's name. + */ + public final void setNameIndex(final int nameIndex) { + this.name_index = nameIndex; + } + + /** + * @param signatureIndex Index in constant pool of field signature. + */ + public final void setSignatureIndex(final int signatureIndex) { + this.signature_index = signatureIndex; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/InnerClass.java b/src/main/java/haidnor/jvm/bcel/classfile/InnerClass.java new file mode 100644 index 0000000..719e8ba --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/InnerClass.java @@ -0,0 +1,193 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class represents a inner class attribute, i.e., the class indices of the inner and outer classes, the name and + * the attributes of the inner class. + * + * @see InnerClasses + */ +public final class InnerClass implements Cloneable, Node { + + private int innerClassIndex; + private int outerClassIndex; + private int innerNameIndex; + private int innerAccessFlags; + + /** + * Construct object from file stream. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + InnerClass(final DataInput file) throws IOException { + this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort()); + } + + /** + * Initialize from another object. + * + * @param c Source to copy. + */ + public InnerClass(final InnerClass c) { + this(c.getInnerClassIndex(), c.getOuterClassIndex(), c.getInnerNameIndex(), c.getInnerAccessFlags()); + } + + /** + * @param innerClassIndex Class index in constant pool of inner class + * @param outerClassIndex Class index in constant pool of outer class + * @param innerNameIndex Name index in constant pool of inner class + * @param innerAccessFlags Access flags of inner class + */ + public InnerClass(final int innerClassIndex, final int outerClassIndex, final int innerNameIndex, final int innerAccessFlags) { + this.innerClassIndex = innerClassIndex; + this.outerClassIndex = outerClassIndex; + this.innerNameIndex = innerNameIndex; + this.innerAccessFlags = innerAccessFlags; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitInnerClass(this); + } + + /** + * @return deep copy of this object + */ + public InnerClass copy() { + try { + return (InnerClass) clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + /** + * Dump inner class attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + public void dump(final DataOutputStream file) throws IOException { + file.writeShort(innerClassIndex); + file.writeShort(outerClassIndex); + file.writeShort(innerNameIndex); + file.writeShort(innerAccessFlags); + } + + /** + * @return access flags of inner class. + */ + public int getInnerAccessFlags() { + return innerAccessFlags; + } + + /** + * @return class index of inner class. + */ + public int getInnerClassIndex() { + return innerClassIndex; + } + + /** + * @return name index of inner class. + */ + public int getInnerNameIndex() { + return innerNameIndex; + } + + /** + * @return class index of outer class. + */ + public int getOuterClassIndex() { + return outerClassIndex; + } + + /** + * @param innerAccessFlags access flags for this inner class + */ + public void setInnerAccessFlags(final int innerAccessFlags) { + this.innerAccessFlags = innerAccessFlags; + } + + /** + * @param innerClassIndex index into the constant pool for this class + */ + public void setInnerClassIndex(final int innerClassIndex) { + this.innerClassIndex = innerClassIndex; + } + + /** + * @param innerNameIndex index into the constant pool for this class's name + */ + public void setInnerNameIndex(final int innerNameIndex) { // TODO unused + this.innerNameIndex = innerNameIndex; + } + + /** + * @param outerClassIndex index into the constant pool for the owning class + */ + public void setOuterClassIndex(final int outerClassIndex) { // TODO unused + this.outerClassIndex = outerClassIndex; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + return "InnerClass(" + innerClassIndex + ", " + outerClassIndex + ", " + innerNameIndex + ", " + innerAccessFlags + ")"; + } + + /** + * @return Resolved string representation + */ + public String toString(final ConstantPool constantPool) { + String outerClassName; + String innerName; + String innerClassName = constantPool.getConstantString(innerClassIndex, Const.CONSTANT_Class); + innerClassName = Utility.compactClassName(innerClassName, false); + if (outerClassIndex != 0) { + outerClassName = constantPool.getConstantString(outerClassIndex, Const.CONSTANT_Class); + outerClassName = " of class " + Utility.compactClassName(outerClassName, false); + } else { + outerClassName = ""; + } + if (innerNameIndex != 0) { + innerName = constantPool.getConstantUtf8(innerNameIndex).getBytes(); + } else { + innerName = "(anonymous)"; + } + String access = Utility.accessToString(innerAccessFlags, true); + access = access.isEmpty() ? "" : access + " "; + return " " + access + innerName + "=class " + innerClassName + outerClassName; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/InnerClasses.java b/src/main/java/haidnor/jvm/bcel/classfile/InnerClasses.java new file mode 100644 index 0000000..398d2c1 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/InnerClasses.java @@ -0,0 +1,156 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.stream.Stream; + +/** + * This class is derived from Attribute and denotes that this class is an Inner class of another. to the source + * file of this class. It is instantiated from the Attribute.readAttribute() method. + * + * @see Attribute + */ +public final class InnerClasses extends Attribute implements Iterable { + + /** + * Empty array. + */ + private static final InnerClass[] EMPTY_INNER_CLASSE_ARRAY = {}; + + private InnerClass[] innerClasses; + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a + * physical copy. + * + * @param c Source to copy. + */ + public InnerClasses(final InnerClasses c) { + this(c.getNameIndex(), c.getLength(), c.getInnerClasses(), c.getConstantPool()); + } + + /** + * Construct object from input stream. + * + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + InnerClasses(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (InnerClass[]) null, constantPool); + final int classCount = input.readUnsignedShort(); + innerClasses = new InnerClass[classCount]; + for (int i = 0; i < classCount; i++) { + innerClasses[i] = new InnerClass(input); + } + } + + /** + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param innerClasses array of inner classes attributes + * @param constantPool Array of constants + */ + public InnerClasses(final int nameIndex, final int length, final InnerClass[] innerClasses, final ConstantPool constantPool) { + super(Const.ATTR_INNER_CLASSES, nameIndex, length, constantPool); + this.innerClasses = innerClasses != null ? innerClasses : EMPTY_INNER_CLASSE_ARRAY; + Args.requireU2(this.innerClasses.length, "innerClasses.length"); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitInnerClasses(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + // TODO this could be recoded to use a lower level constructor after creating a copy of the inner classes + final InnerClasses c = (InnerClasses) clone(); + c.innerClasses = new InnerClass[innerClasses.length]; + Arrays.setAll(c.innerClasses, i -> innerClasses[i].copy()); + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump source file attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(innerClasses.length); + for (final InnerClass innerClass : innerClasses) { + innerClass.dump(file); + } + } + + /** + * @return array of inner class "records" + */ + public InnerClass[] getInnerClasses() { + return innerClasses; + } + + @Override + public Iterator iterator() { + return Stream.of(innerClasses).iterator(); + } + + /** + * @param innerClasses the array of inner classes + */ + public void setInnerClasses(final InnerClass[] innerClasses) { + this.innerClasses = innerClasses != null ? innerClasses : EMPTY_INNER_CLASSE_ARRAY; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + buf.append("InnerClasses("); + buf.append(innerClasses.length); + buf.append("):\n"); + for (final InnerClass innerClass : innerClasses) { + buf.append(innerClass.toString(super.getConstantPool())).append("\n"); + } + return buf.substring(0, buf.length() - 1); // remove the last newline + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/JavaClass.java b/src/main/java/haidnor/jvm/bcel/classfile/JavaClass.java new file mode 100644 index 0000000..c0076b8 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/JavaClass.java @@ -0,0 +1,862 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.generic.ClassGen; +import haidnor.jvm.bcel.generic.Type; +import haidnor.jvm.bcel.util.BCELComparator; +import haidnor.jvm.bcel.util.ClassQueue; +import haidnor.jvm.bcel.util.Repository; +import haidnor.jvm.bcel.util.SyntheticRepository; +import org.apache.commons.lang3.ArrayUtils; + +import java.io.*; +import java.util.*; + +/** + * Represents a Java class, i.e., the data structures, constant pool, fields, methods and commands contained in a Java + * .class file. See JVM specification for details. The intent of + * this class is to represent a parsed or otherwise existing class file. Those interested in programmatically generating + * classes should see the ClassGen class. + * + * @see ClassGen + */ +public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable { + + /** + * The standard class file extension. + * + * @since 6.7.0 + */ + public static final String EXTENSION = ".class"; + + /** + * Empty array. + * + * @since 6.6.0 + */ + public static final JavaClass[] EMPTY_ARRAY = {}; + + public static final byte HEAP = 1; + public static final byte FILE = 2; + public static final byte ZIP = 3; + private static final boolean debug = Boolean.getBoolean("JavaClass.debug"); // Debugging on/off + private static BCELComparator bcelComparator = new BCELComparator() { + + @Override + public boolean equals(final Object o1, final Object o2) { + final JavaClass THIS = (JavaClass) o1; + final JavaClass THAT = (JavaClass) o2; + return Objects.equals(THIS.getClassName(), THAT.getClassName()); + } + + @Override + public int hashCode(final Object o) { + final JavaClass THIS = (JavaClass) o; + return THIS.getClassName().hashCode(); + } + }; + + /* + * Print debug information depending on 'JavaClass.debug' + */ + static void Debug(final String str) { + if (debug) { + System.out.println(str); + } + } + + /** + * @return Comparison strategy object + */ + public static BCELComparator getComparator() { + return bcelComparator; + } + + private static String indent(final Object obj) { + final StringTokenizer tokenizer = new StringTokenizer(obj.toString(), "\n"); + final StringBuilder buf = new StringBuilder(); + while (tokenizer.hasMoreTokens()) { + buf.append("\t").append(tokenizer.nextToken()).append("\n"); + } + return buf.toString(); + } + + /** + * @param comparator Comparison strategy object + */ + public static void setComparator(final BCELComparator comparator) { + bcelComparator = comparator; + } + + private String fileName; + private final String packageName; + private String sourceFileName = ""; + private int classNameIndex; + private int superclassNameIndex; + private String className; + private String superclassName; + private int major; + private int minor; // Compiler version + private ConstantPool constantPool; // Constant pool + private int[] interfaces; // implemented interfaces + private String[] interfaceNames; + private JavaField[] fields; // Fields, i.e., variables of class + private JavaMethod[] methods; // methods defined in the class + private Attribute[] attributes; // attributes defined in the class + + private AnnotationEntry[] annotations; // annotations defined on the class + private byte source = HEAP; // Generated in memory + + private boolean isAnonymous; + + private boolean isNested; + + private boolean computedNestedTypeStatus; + + /** + * In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any + * better. + */ + private transient Repository repository = SyntheticRepository.getInstance(); + + /** + * Constructor gets all contents as arguments. + * + * @param classNameIndex Class name + * @param superclassNameIndex Superclass name + * @param fileName File name + * @param major Major compiler version + * @param minor Minor compiler version + * @param accessFlags Access rights defined by bit flags + * @param constantPool Array of constants + * @param interfaces Implemented interfaces + * @param fields Class fields + * @param methods Class methods + * @param attributes Class attributes + */ + public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags, + final ConstantPool constantPool, final int[] interfaces, final JavaField[] fields, final JavaMethod[] methods, final Attribute[] attributes) { + this(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes, HEAP); + } + + /** + * Constructor gets all contents as arguments. + * + * @param classNameIndex Index into constant pool referencing a ConstantClass that represents this class. + * @param superclassNameIndex Index into constant pool referencing a ConstantClass that represents this class's + * superclass. + * @param fileName File name + * @param major Major compiler version + * @param minor Minor compiler version + * @param accessFlags Access rights defined by bit flags + * @param constantPool Array of constants + * @param interfaces Implemented interfaces + * @param fields Class fields + * @param methods Class methods + * @param attributes Class attributes + * @param source Read from file or generated in memory? + */ + public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags, + final ConstantPool constantPool, int[] interfaces, JavaField[] fields, JavaMethod[] methods, Attribute[] attributes, final byte source) { + super(accessFlags); + if (interfaces == null) { + interfaces = ArrayUtils.EMPTY_INT_ARRAY; + } + if (attributes == null) { + attributes = Attribute.EMPTY_ARRAY; + } + if (fields == null) { + fields = JavaField.EMPTY_FIELD_ARRAY; + } + if (methods == null) { + methods = JavaMethod.EMPTY_METHOD_ARRAY; + } + this.classNameIndex = classNameIndex; + this.superclassNameIndex = superclassNameIndex; + this.fileName = fileName; + this.major = major; + this.minor = minor; + this.constantPool = constantPool; + this.interfaces = interfaces; + this.fields = fields; + this.methods = methods; + this.attributes = attributes; + this.source = source; + // Get source file name if available + for (final Attribute attribute : attributes) { + if (attribute instanceof SourceFile) { + sourceFileName = ((SourceFile) attribute).getSourceFileName(); + break; + } + } + /* + * According to the specification the following entries must be of type 'ConstantClass' but we check that anyway via the + * 'ConstPool.getConstant' method. + */ + className = constantPool.getConstantString(classNameIndex, Const.CONSTANT_Class); + className = Utility.compactClassName(className, false); + final int index = className.lastIndexOf('.'); + if (index < 0) { + packageName = ""; + } else { + packageName = className.substring(0, index); + } + if (superclassNameIndex > 0) { + // May be zero -> class is java.lang.Object + superclassName = constantPool.getConstantString(superclassNameIndex, Const.CONSTANT_Class); + superclassName = Utility.compactClassName(superclassName, false); + } else { + superclassName = "java.lang.Object"; + } + interfaceNames = new String[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + final String str = constantPool.getConstantString(interfaces[i], Const.CONSTANT_Class); + interfaceNames[i] = Utility.compactClassName(str, false); + } + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitJavaClass(this); + } + + /** + * Return the natural ordering of two JavaClasses. This ordering is based on the class name + * + * @since 6.0 + */ + @Override + public int compareTo(final JavaClass obj) { + return getClassName().compareTo(obj.getClassName()); + } + + private void computeNestedTypeStatus() { + if (computedNestedTypeStatus) { + return; + } + for (final Attribute attribute : this.attributes) { + if (attribute instanceof InnerClasses) { + ((InnerClasses) attribute).forEach(innerClass -> { + boolean innerClassAttributeRefersToMe = false; + String innerClassName = constantPool.getConstantString(innerClass.getInnerClassIndex(), Const.CONSTANT_Class); + innerClassName = Utility.compactClassName(innerClassName, false); + if (innerClassName.equals(getClassName())) { + innerClassAttributeRefersToMe = true; + } + if (innerClassAttributeRefersToMe) { + this.isNested = true; + if (innerClass.getInnerNameIndex() == 0) { + this.isAnonymous = true; + } + } + }); + } + } + this.computedNestedTypeStatus = true; + } + + /** + * @return deep copy of this class + */ + public JavaClass copy() { + try { + final JavaClass c = (JavaClass) clone(); + c.constantPool = constantPool.copy(); + c.interfaces = interfaces.clone(); + c.interfaceNames = interfaceNames.clone(); + c.fields = new JavaField[fields.length]; + Arrays.setAll(c.fields, i -> fields[i].copy(c.constantPool)); + c.methods = new JavaMethod[methods.length]; + Arrays.setAll(c.methods, i -> methods[i].copy(c.constantPool)); + c.attributes = new Attribute[attributes.length]; + Arrays.setAll(c.attributes, i -> attributes[i].copy(c.constantPool)); + return c; + } catch (final CloneNotSupportedException e) { + return null; + } + } + + /** + * Dump Java class to output stream in binary format. + * + * @param file Output stream + * @throws IOException if an I/O error occurs. + */ + public void dump(final DataOutputStream file) throws IOException { + file.writeInt(Const.JVM_CLASSFILE_MAGIC); + file.writeShort(minor); + file.writeShort(major); + constantPool.dump(file); + file.writeShort(super.getAccessFlags()); + file.writeShort(classNameIndex); + file.writeShort(superclassNameIndex); + file.writeShort(interfaces.length); + for (final int interface1 : interfaces) { + file.writeShort(interface1); + } + file.writeShort(fields.length); + for (final JavaField field : fields) { + field.dump(file); + } + file.writeShort(methods.length); + for (final JavaMethod method : methods) { + method.dump(file); + } + if (attributes != null) { + file.writeShort(attributes.length); + for (final Attribute attribute : attributes) { + attribute.dump(file); + } + } else { + file.writeShort(0); + } + file.flush(); + } + + /** + * Dump class to a file. + * + * @param file Output file + * @throws IOException if an I/O error occurs. + */ + public void dump(final File file) throws IOException { + final String parent = file.getParent(); + if (parent != null) { + final File dir = new File(parent); + if (!dir.mkdirs() && !dir.isDirectory()) { + throw new IOException("Could not create the directory " + dir); + } + } + try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) { + dump(dos); + } + } + + /** + * Dump Java class to output stream in binary format. + * + * @param file Output stream + * @throws IOException if an I/O error occurs. + */ + public void dump(final OutputStream file) throws IOException { + dump(new DataOutputStream(file)); + } + + /** + * Dump class to a file named fileName. + * + * @param fileName Output file name + * @throws IOException if an I/O error occurs. + */ + public void dump(final String fileName) throws IOException { + dump(new File(fileName)); + } + + /** + * Return value as defined by given BCELComparator strategy. By default two JavaClass objects are said to be equal when + * their class names are equal. + * + * @see Object#equals(Object) + */ + @Override + public boolean equals(final Object obj) { + return bcelComparator.equals(this, obj); + } + + /** + * Get all interfaces implemented by this JavaClass (transitively). + * + * @throws ClassNotFoundException if any of the class's superclasses or interfaces can't be found. + */ + public JavaClass[] getAllInterfaces() throws ClassNotFoundException { + final ClassQueue queue = new ClassQueue(); + final Set allInterfaces = new TreeSet<>(); + queue.enqueue(this); + while (!queue.empty()) { + final JavaClass clazz = queue.dequeue(); + final JavaClass souper = clazz.getSuperClass(); + final JavaClass[] interfaces = clazz.getInterfaces(); + if (clazz.isInterface()) { + allInterfaces.add(clazz); + } else if (souper != null) { + queue.enqueue(souper); + } + for (final JavaClass iface : interfaces) { + queue.enqueue(iface); + } + } + return allInterfaces.toArray(JavaClass.EMPTY_ARRAY); + } + + /** + * @return Annotations on the class + * @since 6.0 + */ + public AnnotationEntry[] getAnnotationEntries() { + if (annotations == null) { + annotations = AnnotationEntry.createAnnotationEntries(getAttributes()); + } + + return annotations; + } + + /** + * @return Attributes of the class. + */ + public Attribute[] getAttributes() { + return attributes; + } + + /** + * @return class in binary format + */ + public byte[] getBytes() { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos)) { + dump(dos); + } catch (final IOException e) { + e.printStackTrace(); + } + return baos.toByteArray(); + } + + /** + * @return Class name. + */ + public String getClassName() { + return className; + } + + /** + * @return Class name index. + */ + public int getClassNameIndex() { + return classNameIndex; + } + + /** + * @return Constant pool. + */ + public ConstantPool getConstantPool() { + return constantPool; + } + + /** + * @return Fields, i.e., variables of the class. Like the JVM spec mandates for the classfile format, these fields are + * those specific to this class, and not those of the superclass or superinterfaces. + */ + public JavaField[] getFields() { + return fields; + } + + /** + * @return File name of class, aka SourceFile attribute value + */ + public String getFileName() { + return fileName; + } + + /** + * @return Indices in constant pool of implemented interfaces. + */ + public int[] getInterfaceIndices() { + return interfaces; + } + + /** + * @return Names of implemented interfaces. + */ + public String[] getInterfaceNames() { + return interfaceNames; + } + + /** + * Get interfaces directly implemented by this JavaClass. + * + * @throws ClassNotFoundException if any of the class's interfaces can't be found. + */ + public JavaClass[] getInterfaces() throws ClassNotFoundException { + final String[] interfaces = getInterfaceNames(); + final JavaClass[] classes = new JavaClass[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + classes[i] = repository.loadClass(interfaces[i]); + } + return classes; + } + + /** + * @return Major number of class file version. + */ + public int getMajor() { + return major; + } + + /** + * @return A {@link JavaMethod} corresponding to java.lang.reflect.Method if any + */ + public JavaMethod getMethod(final java.lang.reflect.Method m) { + for (final JavaMethod method : methods) { + if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) { + return method; + } + } + return null; + } + + /** + * @return Methods of the class. + */ + public JavaMethod[] getMethods() { + return methods; + } + + /** + * @return Minor number of class file version. + */ + public int getMinor() { + return minor; + } + + /** + * @return Package name. + */ + public String getPackageName() { + return packageName; + } + + /** + * Gets the ClassRepository which holds its definition. By default this is the same as + * SyntheticRepository.getInstance(); + */ + public Repository getRepository() { + return repository; + } + + /** + * @return returns either HEAP (generated), FILE, or ZIP + */ + public final byte getSource() { + return source; + } + + /** + * @return file name where this class was read from + */ + public String getSourceFileName() { + return sourceFileName; + } + + /** + * Gets the source file path including the package path. + * + * @return path to original source file of parsed class, relative to original source directory. + * @since 6.7.0 + */ + public String getSourceFilePath() { + final StringBuilder outFileName = new StringBuilder(); + if (!packageName.isEmpty()) { + outFileName.append(Utility.packageToPath(packageName)); + outFileName.append('/'); + } + outFileName.append(sourceFileName); + return outFileName.toString(); + } + + /** + * @return the superclass for this JavaClass object, or null if this is java.lang.Object + * @throws ClassNotFoundException if the superclass can't be found + */ + public JavaClass getSuperClass() throws ClassNotFoundException { + if ("java.lang.Object".equals(getClassName())) { + return null; + } + return repository.loadClass(getSuperclassName()); + } + + /** + * @return list of super classes of this class in ascending order, i.e., java.lang.Object is always the last element + * @throws ClassNotFoundException if any of the superclasses can't be found + */ + public JavaClass[] getSuperClasses() throws ClassNotFoundException { + JavaClass clazz = this; + final List allSuperClasses = new ArrayList<>(); + for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) { + allSuperClasses.add(clazz); + } + return allSuperClasses.toArray(JavaClass.EMPTY_ARRAY); + } + + /** + * returns the super class name of this class. In the case that this class is java.lang.Object, it will return itself + * (java.lang.Object). This is probably incorrect but isn't fixed at this time to not break existing clients. + * + * @return Superclass name. + */ + public String getSuperclassName() { + return superclassName; + } + + /** + * @return Class name index. + */ + public int getSuperclassNameIndex() { + return superclassNameIndex; + } + + /** + * Return value as defined by given BCELComparator strategy. By default return the hashcode of the class name. + * + * @see Object#hashCode() + */ + @Override + public int hashCode() { + return bcelComparator.hashCode(this); + } + + /** + * @return true, if this class is an implementation of interface inter + * @throws ClassNotFoundException if superclasses or superinterfaces of this class can't be found + */ + public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException { + if (!inter.isInterface()) { + throw new IllegalArgumentException(inter.getClassName() + " is no interface"); + } + if (this.equals(inter)) { + return true; + } + final JavaClass[] superInterfaces = getAllInterfaces(); + for (final JavaClass superInterface : superInterfaces) { + if (superInterface.equals(inter)) { + return true; + } + } + return false; + } + + /** + * Equivalent to runtime "instanceof" operator. + * + * @return true if this JavaClass is derived from the super class + * @throws ClassNotFoundException if superclasses or superinterfaces of this object can't be found + */ + public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException { + if (this.equals(superclass)) { + return true; + } + for (final JavaClass clazz : getSuperClasses()) { + if (clazz.equals(superclass)) { + return true; + } + } + if (superclass.isInterface()) { + return implementationOf(superclass); + } + return false; + } + + /** + * @since 6.0 + */ + public final boolean isAnonymous() { + computeNestedTypeStatus(); + return this.isAnonymous; + } + + public final boolean isClass() { + return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0; + } + + /** + * @since 6.0 + */ + public final boolean isNested() { + computeNestedTypeStatus(); + return this.isNested; + } + + public final boolean isSuper() { + return (super.getAccessFlags() & Const.ACC_SUPER) != 0; + } + + /** + * @param attributes . + */ + public void setAttributes(final Attribute[] attributes) { + this.attributes = attributes; + } + + /** + * @param className . + */ + public void setClassName(final String className) { + this.className = className; + } + + /** + * @param classNameIndex . + */ + public void setClassNameIndex(final int classNameIndex) { + this.classNameIndex = classNameIndex; + } + + /** + * @param constantPool . + */ + public void setConstantPool(final ConstantPool constantPool) { + this.constantPool = constantPool; + } + + /** + * @param fields . + */ + public void setFields(final JavaField[] fields) { + this.fields = fields; + } + + /** + * Set File name of class, aka SourceFile attribute value + */ + public void setFileName(final String fileName) { + this.fileName = fileName; + } + + /** + * @param interfaceNames . + */ + public void setInterfaceNames(final String[] interfaceNames) { + this.interfaceNames = interfaceNames; + } + + /** + * @param interfaces . + */ + public void setInterfaces(final int[] interfaces) { + this.interfaces = interfaces; + } + + /** + * @param major . + */ + public void setMajor(final int major) { + this.major = major; + } + + /** + * @param methods . + */ + public void setMethods(final JavaMethod[] methods) { + this.methods = methods; + } + + /** + * @param minor . + */ + public void setMinor(final int minor) { + this.minor = minor; + } + + /** + * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done. + */ + public void setRepository(final Repository repository) { // TODO make protected? + this.repository = repository; + } + + /** + * Set absolute path to file this class was read from. + */ + public void setSourceFileName(final String sourceFileName) { + this.sourceFileName = sourceFileName; + } + + /** + * @param superclassName . + */ + public void setSuperclassName(final String superclassName) { + this.superclassName = superclassName; + } + + /** + * @param superclassNameIndex . + */ + public void setSuperclassNameIndex(final int superclassNameIndex) { + this.superclassNameIndex = superclassNameIndex; + } + + /** + * @return String representing class contents. + */ + @Override + public String toString() { + String access = Utility.accessToString(super.getAccessFlags(), true); + access = access.isEmpty() ? "" : access + " "; + final StringBuilder buf = new StringBuilder(128); + buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ") + .append(Utility.compactClassName(superclassName, false)).append('\n'); + final int size = interfaces.length; + if (size > 0) { + buf.append("implements\t\t"); + for (int i = 0; i < size; i++) { + buf.append(interfaceNames[i]); + if (i < size - 1) { + buf.append(", "); + } + } + buf.append('\n'); + } + buf.append("file name\t\t").append(fileName).append('\n'); + buf.append("compiled from\t\t").append(sourceFileName).append('\n'); + buf.append("compiler version\t").append(major).append(".").append(minor).append('\n'); + buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n'); + buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n"); + buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n"); + if (attributes.length > 0) { + buf.append("\nAttribute(s):\n"); + for (final Attribute attribute : attributes) { + buf.append(indent(attribute)); + } + } + final AnnotationEntry[] annotations = getAnnotationEntries(); + if (annotations != null && annotations.length > 0) { + buf.append("\nAnnotation(s):\n"); + for (final AnnotationEntry annotation : annotations) { + buf.append(indent(annotation)); + } + } + if (fields.length > 0) { + buf.append("\n").append(fields.length).append(" fields:\n"); + for (final JavaField field : fields) { + buf.append("\t").append(field).append('\n'); + } + } + if (methods.length > 0) { + buf.append("\n").append(methods.length).append(" methods:\n"); + for (final JavaMethod method : methods) { + buf.append("\t").append(method).append('\n'); + } + } + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/JavaField.java b/src/main/java/haidnor/jvm/bcel/classfile/JavaField.java new file mode 100644 index 0000000..cf1f7fd --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/JavaField.java @@ -0,0 +1,193 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.generic.Type; +import haidnor.jvm.bcel.util.BCELComparator; + +import java.io.DataInput; +import java.io.IOException; +import java.util.Objects; + +/** + * This class represents the field info structure, i.e., the representation for a variable in the class. See JVM + * specification for details. + */ +public final class JavaField extends FieldOrMethod { + + /** + * Empty array constant. + * + * @since 6.6.0 + */ + public static final JavaField[] EMPTY_ARRAY = {}; + + private static BCELComparator bcelComparator = new BCELComparator() { + + @Override + public boolean equals(final Object o1, final Object o2) { + final JavaField THIS = (JavaField) o1; + final JavaField THAT = (JavaField) o2; + return Objects.equals(THIS.getName(), THAT.getName()) && Objects.equals(THIS.getSignature(), THAT.getSignature()); + } + + @Override + public int hashCode(final Object o) { + final JavaField THIS = (JavaField) o; + return THIS.getSignature().hashCode() ^ THIS.getName().hashCode(); + } + }; + + /** + * Empty array. + */ + static final JavaField[] EMPTY_FIELD_ARRAY = {}; + + /** + * @return Comparison strategy object + */ + public static BCELComparator getComparator() { + return bcelComparator; + } + + /** + * @param comparator Comparison strategy object + */ + public static void setComparator(final BCELComparator comparator) { + bcelComparator = comparator; + } + + /** + * Construct object from file stream. + * + * @param file Input stream + */ + JavaField(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException { + super(file, constantPool); + } + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a + * physical copy. + * + * @param c Source to copy. + */ + public JavaField(final JavaField c) { + super(c); + } + + /** + * @param accessFlags Access rights of field + * @param nameIndex Points to field name in constant pool + * @param signatureIndex Points to encoded signature + * @param attributes Collection of attributes + * @param constantPool Array of constants + */ + public JavaField(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes, final ConstantPool constantPool) { + super(accessFlags, nameIndex, signatureIndex, attributes, constantPool); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitField(this); + } + + /** + * @return deep copy of this field + */ + public JavaField copy(final ConstantPool constantPool) { + return (JavaField) copy_(constantPool); + } + + /** + * Return value as defined by given BCELComparator strategy. By default two Field objects are said to be equal when + * their names and signatures are equal. + * + * @see Object#equals(Object) + */ + @Override + public boolean equals(final Object obj) { + return bcelComparator.equals(this, obj); + } + + /** + * @return constant value associated with this field (may be null) + */ + public ConstantValue getConstantValue() { + for (final Attribute attribute : super.getAttributes()) { + if (attribute.getTag() == Const.ATTR_CONSTANT_VALUE) { + return (ConstantValue) attribute; + } + } + return null; + } + + /** + * @return type of field + */ + public Type getType() { + return Type.getReturnType(getSignature()); + } + + /** + * Return value as defined by given BCELComparator strategy. By default return the hashcode of the field's name XOR + * signature. + * + * @see Object#hashCode() + */ + @Override + public int hashCode() { + return bcelComparator.hashCode(this); + } + + /** + * Return string representation close to declaration format, 'public static final short MAX = 100', e.g.. + * + * @return String representation of field, including the signature. + */ + @Override + public String toString() { + String name; + String signature; + String access; // Short cuts to constant pool + + // Get names from constant pool + access = Utility.accessToString(super.getAccessFlags()); + access = access.isEmpty() ? "" : access + " "; + signature = Utility.signatureToString(getSignature()); + name = getName(); + final StringBuilder buf = new StringBuilder(64); // CHECKSTYLE IGNORE MagicNumber + buf.append(access).append(signature).append(" ").append(name); + final ConstantValue cv = getConstantValue(); + if (cv != null) { + buf.append(" = ").append(cv); + } + for (final Attribute attribute : super.getAttributes()) { + if (!(attribute instanceof ConstantValue)) { + buf.append(" [").append(attribute).append("]"); + } + } + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/JavaMethod.java b/src/main/java/haidnor/jvm/bcel/classfile/JavaMethod.java new file mode 100644 index 0000000..d83f245 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/JavaMethod.java @@ -0,0 +1,257 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.generic.Type; +import haidnor.jvm.bcel.util.BCELComparator; + +import java.io.DataInput; +import java.io.IOException; +import java.util.Objects; + +/** + * This class represents the method info structure, i.e., the representation for a method in the class. See JVM + * specification for details. A method has access flags, a name, a signature and a number of attributes. + */ +public final class JavaMethod extends FieldOrMethod { + + /** + * Empty array constant. + * + * @since 6.6.0 + */ + public static final JavaMethod[] EMPTY_ARRAY = {}; + + private static BCELComparator bcelComparator = new BCELComparator() { + + @Override + public boolean equals(final Object o1, final Object o2) { + final JavaMethod THIS = (JavaMethod) o1; + final JavaMethod THAT = (JavaMethod) o2; + return Objects.equals(THIS.getName(), THAT.getName()) && Objects.equals(THIS.getSignature(), THAT.getSignature()); + } + + @Override + public int hashCode(final Object o) { + final JavaMethod THIS = (JavaMethod) o; + return THIS.getSignature().hashCode() ^ THIS.getName().hashCode(); + } + }; + + /** + * Empty array. + */ + static final JavaMethod[] EMPTY_METHOD_ARRAY = {}; + + /** + * @return Comparison strategy object + */ + public static BCELComparator getComparator() { + return bcelComparator; + } + + /** + * @param comparator Comparison strategy object + */ + public static void setComparator(final BCELComparator comparator) { + bcelComparator = comparator; + } + + // annotations defined on the parameters of a method + private ParameterAnnotationEntry[] parameterAnnotationEntries; + + /** + * Empty constructor, all attributes have to be defined via 'setXXX' methods. Use at your own risk. + */ + public JavaMethod() { + } + + /** + * Construct object from file stream. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + JavaMethod(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException { + super(file, constantPool); + } + + /** + * @param accessFlags Access rights of method + * @param nameIndex Points to field name in constant pool + * @param signatureIndex Points to encoded signature + * @param attributes Collection of attributes + * @param constantPool Array of constants + */ + public JavaMethod(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes, final ConstantPool constantPool) { + super(accessFlags, nameIndex, signatureIndex, attributes, constantPool); + } + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a + * physical copy. + * + * @param c Source to copy. + */ + public JavaMethod(final JavaMethod c) { + super(c); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitMethod(this); + } + + /** + * @return deep copy of this method + */ + public JavaMethod copy(final ConstantPool constantPool) { + return (JavaMethod) copy_(constantPool); + } + + /** + * Return value as defined by given BCELComparator strategy. By default two method objects are said to be equal when + * their names and signatures are equal. + * + * @see Object#equals(Object) + */ + @Override + public boolean equals(final Object obj) { + return bcelComparator.equals(this, obj); + } + + /** + * @return array of method argument types + */ + public Type[] getArgumentTypes() { + return Type.getArgumentTypes(getSignature()); + } + + /** + * @return Code attribute of method, if any + */ + public Code getCode() { + for (final Attribute attribute : super.getAttributes()) { + if (attribute instanceof Code) { + return (Code) attribute; + } + } + return null; + } + + /** + * @return ExceptionTable attribute of method, if any, i.e., list all exceptions the method may throw not exception + * handlers! + */ + public ExceptionTable getExceptionTable() { + for (final Attribute attribute : super.getAttributes()) { + if (attribute instanceof ExceptionTable) { + return (ExceptionTable) attribute; + } + } + return null; + } + + /** + * @return LineNumberTable of code attribute if any, i.e. the call is forwarded to the Code atribute. + */ + public LineNumberTable getLineNumberTable() { + final Code code = getCode(); + if (code == null) { + return null; + } + return code.getLineNumberTable(); + } + + /** + * @return LocalVariableTable of code attribute if any, i.e. the call is forwarded to the Code atribute. + */ + public LocalVariableTable getLocalVariableTable() { + final Code code = getCode(); + if (code == null) { + return null; + } + return code.getLocalVariableTable(); + } + + /** + * @return Annotations on the parameters of a method + * @since 6.0 + */ + public ParameterAnnotationEntry[] getParameterAnnotationEntries() { + if (parameterAnnotationEntries == null) { + parameterAnnotationEntries = ParameterAnnotationEntry.createParameterAnnotationEntries(getAttributes()); + } + return parameterAnnotationEntries; + } + + /** + * @return return type of method + */ + public Type getReturnType() { + return Type.getReturnType(getSignature()); + } + + /** + * Return value as defined by given BCELComparator strategy. By default return the hashcode of the method's name XOR + * signature. + * + * @see Object#hashCode() + */ + @Override + public int hashCode() { + return bcelComparator.hashCode(this); + } + + /** + * Return string representation close to declaration format, 'public static void main(String[] args) throws + * IOException', e.g. + * + * @return String representation of the method. + */ + @Override + public String toString() { + final String access = Utility.accessToString(super.getAccessFlags()); + // Get name and signature from constant pool + ConstantUtf8 c = super.getConstantPool().getConstantUtf8(super.getSignatureIndex()); + String signature = c.getBytes(); + c = super.getConstantPool().getConstantUtf8(super.getNameIndex()); + final String name = c.getBytes(); + signature = Utility.methodSignatureToString(signature, name, access, true, getLocalVariableTable()); + final StringBuilder buf = new StringBuilder(signature); + for (final Attribute attribute : super.getAttributes()) { + if (!(attribute instanceof Code || attribute instanceof ExceptionTable)) { + buf.append(" [").append(attribute).append("]"); + } + } + final ExceptionTable e = getExceptionTable(); + if (e != null) { + final String str = e.toString(); + if (!str.isEmpty()) { + buf.append("\n\t\tthrows ").append(str); + } + } + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/LineNumber.java b/src/main/java/haidnor/jvm/bcel/classfile/LineNumber.java new file mode 100644 index 0000000..8f45505 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/LineNumber.java @@ -0,0 +1,138 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class represents a (PC offset, line number) pair, i.e., a line number in the source that corresponds to a + * relative address in the byte code. This is used for debugging purposes. + * + * @see LineNumberTable + */ +public final class LineNumber implements Cloneable, Node { + + static final LineNumber[] EMPTY_ARRAY = {}; + + /** Program Counter (PC) corresponds to line */ + private int startPc; + + /** number in source file */ + private int lineNumber; + + /** + * Construct object from file stream. + * + * @param file Input stream + * @throws IOException if an I/O Exception occurs in readUnsignedShort + */ + LineNumber(final DataInput file) throws IOException { + this(file.readUnsignedShort(), file.readUnsignedShort()); + } + + /** + * @param startPc Program Counter (PC) corresponds to + * @param lineNumber line number in source file + */ + public LineNumber(final int startPc, final int lineNumber) { + this.startPc = Args.requireU2(startPc, "startPc"); + this.lineNumber = Args.requireU2(lineNumber, "lineNumber"); + } + + /** + * Initialize from another object. + * + * @param c the object to copy + */ + public LineNumber(final LineNumber c) { + this(c.getStartPC(), c.getLineNumber()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitLineNumber(this); + } + + /** + * @return deep copy of this object + */ + public LineNumber copy() { + try { + return (LineNumber) clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + /** + * Dump line number/pc pair to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O Exception occurs in writeShort + */ + public void dump(final DataOutputStream file) throws IOException { + file.writeShort(startPc); + file.writeShort(lineNumber); + } + + /** + * @return Corresponding source line + */ + public int getLineNumber() { + return lineNumber & 0xffff; + } + + /** + * @return PC in code + */ + public int getStartPC() { + return startPc & 0xffff; + } + + /** + * @param lineNumber the source line number + */ + public void setLineNumber(final int lineNumber) { + this.lineNumber = (short) lineNumber; + } + + /** + * @param startPc the pc for this line number + */ + public void setStartPC(final int startPc) { + this.startPc = (short) startPc; + } + + /** + * @return String representation + */ + @Override + public String toString() { + return "LineNumber(" + getStartPC() + ", " + getLineNumber() + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/LineNumberTable.java b/src/main/java/haidnor/jvm/bcel/classfile/LineNumberTable.java new file mode 100644 index 0000000..28ec9f9 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/LineNumberTable.java @@ -0,0 +1,213 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.stream.Stream; + +/** + * This class represents a table of line numbers for debugging purposes. This attribute is used by the Code + * attribute. It contains pairs of PCs and line numbers. + * + * @see Code + * @see LineNumber + */ +public final class LineNumberTable extends Attribute implements Iterable { + + private static final int MAX_LINE_LENGTH = 72; + private LineNumber[] lineNumberTable; // Table of line/numbers pairs + + /** + * Construct object from input stream. + * + * @param nameIndex Index of name + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O Exception occurs in readUnsignedShort + */ + LineNumberTable(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (LineNumber[]) null, constantPool); + final int lineNumberTableLength = input.readUnsignedShort(); + lineNumberTable = new LineNumber[lineNumberTableLength]; + for (int i = 0; i < lineNumberTableLength; i++) { + lineNumberTable[i] = new LineNumber(input); + } + } + + /* + * @param nameIndex Index of name + * + * @param length Content length in bytes + * + * @param lineNumberTable Table of line/numbers pairs + * + * @param constantPool Array of constants + */ + public LineNumberTable(final int nameIndex, final int length, final LineNumber[] lineNumberTable, final ConstantPool constantPool) { + super(Const.ATTR_LINE_NUMBER_TABLE, nameIndex, length, constantPool); + this.lineNumberTable = lineNumberTable != null ? lineNumberTable : LineNumber.EMPTY_ARRAY; + Args.requireU2(this.lineNumberTable.length, "lineNumberTable.length"); + } + + /* + * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a + * physical copy. + */ + public LineNumberTable(final LineNumberTable c) { + this(c.getNameIndex(), c.getLength(), c.getLineNumberTable(), c.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitLineNumberTable(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + // TODO could use the lower level constructor and thereby allow + // lineNumberTable to be made final + final LineNumberTable c = (LineNumberTable) clone(); + c.lineNumberTable = new LineNumber[lineNumberTable.length]; + Arrays.setAll(c.lineNumberTable, i -> lineNumberTable[i].copy()); + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump line number table attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O Exception occurs in writeShort + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(lineNumberTable.length); + for (final LineNumber lineNumber : lineNumberTable) { + lineNumber.dump(file); + } + } + + /** + * @return Array of (pc offset, line number) pairs. + */ + public LineNumber[] getLineNumberTable() { + return lineNumberTable; + } + + /** + * Map byte code positions to source code lines. + * + * @param pos byte code offset + * @return corresponding line in source code + */ + public int getSourceLine(final int pos) { + int l = 0; + int r = lineNumberTable.length - 1; + if (r < 0) { + return -1; + } + int minIndex = -1; + int min = -1; + /* + * Do a binary search since the array is ordered. + */ + do { + final int i = l + r >>> 1; + final int j = lineNumberTable[i].getStartPC(); + if (j == pos) { + return lineNumberTable[i].getLineNumber(); + } + if (pos < j) { + r = i - 1; + } else { + l = i + 1; + } + /* + * If exact match can't be found (which is the most common case) return the line number that corresponds to the greatest + * index less than pos. + */ + if (j < pos && j > min) { + min = j; + minIndex = i; + } + } while (l <= r); + /* + * It's possible that we did not find any valid entry for the bytecode offset we were looking for. + */ + if (minIndex < 0) { + return -1; + } + return lineNumberTable[minIndex].getLineNumber(); + } + + public int getTableLength() { + return lineNumberTable == null ? 0 : lineNumberTable.length; + } + + @Override + public Iterator iterator() { + return Stream.of(lineNumberTable).iterator(); + } + + /** + * @param lineNumberTable the line number entries for this table + */ + public void setLineNumberTable(final LineNumber[] lineNumberTable) { + this.lineNumberTable = lineNumberTable; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + final StringBuilder line = new StringBuilder(); + final String newLine = System.getProperty("line.separator", "\n"); + for (int i = 0; i < lineNumberTable.length; i++) { + line.append(lineNumberTable[i].toString()); + if (i < lineNumberTable.length - 1) { + line.append(", "); + } + if (line.length() > MAX_LINE_LENGTH && i < lineNumberTable.length - 1) { + line.append(newLine); + buf.append(line); + line.setLength(0); + } + } + buf.append(line); + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/LocalVariable.java b/src/main/java/haidnor/jvm/bcel/classfile/LocalVariable.java new file mode 100644 index 0000000..9b6cfc7 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/LocalVariable.java @@ -0,0 +1,282 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class represents a local variable within a method. It contains its scope, name, signature and index on the + * method's frame. It is used both to represent an element of the LocalVariableTable as well as an element of the + * LocalVariableTypeTable. The nomenclature used here may be a bit confusing; while the two items have the same layout + * in a class file, a LocalVariableTable attribute contains a descriptor_index, not a signatureIndex. The + * LocalVariableTypeTable attribute does have a signatureIndex. + * + * @see Utility for more details on the difference. + * @see LocalVariableTable + * @see LocalVariableTypeTable + */ +public final class LocalVariable implements Cloneable, Node { + + static final LocalVariable[] EMPTY_ARRAY = {}; + + /** + * Range in which the variable is valid. + */ + private int startPc; + + private int length; + + /** + * Index in constant pool of variable name. + */ + private int nameIndex; + + /** + * Technically, a decscriptor_index for a local variable table entry and a signatureIndex for a local variable type table entry. Index of variable signature + */ + private int signatureIndex; + + /* + * Variable is index'th local variable on this method's frame. + */ + private int index; + + private ConstantPool constantPool; + + /** + * Never changes; used to match up with LocalVariableTypeTable entries. + */ + private final int origIndex; + + /** + * Constructs object from file stream. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + LocalVariable(final DataInput file, final ConstantPool constantPool) throws IOException { + this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), constantPool); + } + + /** + * @param startPc Range in which the variable + * @param length ... is valid + * @param nameIndex Index in constant pool of variable name + * @param signatureIndex Index of variable's signature + * @param index Variable is 'index'th local variable on the method's frame + * @param constantPool Array of constants + */ + public LocalVariable(final int startPc, final int length, final int nameIndex, final int signatureIndex, final int index, final ConstantPool constantPool) { + this(startPc, length, nameIndex, signatureIndex, index, constantPool, index); + } + + /** + * @param startPc Range in which the variable + * @param length ... is valid + * @param nameIndex Index in constant pool of variable name + * @param signatureIndex Index of variable's signature + * @param index Variable is 'index'th local variable on the method's frame + * @param constantPool Array of constants + * @param origIndex Variable is 'index'th local variable on the method's frame prior to any changes + */ + public LocalVariable(final int startPc, final int length, final int nameIndex, final int signatureIndex, final int index, final ConstantPool constantPool, + final int origIndex) { + this.startPc = Args.requireU2(startPc, "startPc"); + this.length = Args.requireU2(length, "length"); + this.nameIndex = Args.requireU2(nameIndex, "nameIndex"); + this.signatureIndex = Args.requireU2(signatureIndex, "signatureIndex"); + this.index = Args.requireU2(index, "index"); + this.origIndex = Args.requireU2(origIndex, "origIndex"); + this.constantPool = constantPool; + } + + /** + * Initializes from another LocalVariable. Note that both objects use the same references (shallow copy). Use copy() for + * a physical copy. + * + * @param localVariable Another LocalVariable. + */ + public LocalVariable(final LocalVariable localVariable) { + this(localVariable.getStartPC(), localVariable.getLength(), localVariable.getNameIndex(), localVariable.getSignatureIndex(), localVariable.getIndex(), + localVariable.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitLocalVariable(this); + } + + /** + * @return deep copy of this object + */ + public LocalVariable copy() { + try { + return (LocalVariable) clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + /** + * Dumps local variable to file stream in binary format. + * + * @param dataOutputStream Output file stream + * @throws IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public void dump(final DataOutputStream dataOutputStream) throws IOException { + dataOutputStream.writeShort(startPc); + dataOutputStream.writeShort(length); + dataOutputStream.writeShort(nameIndex); + dataOutputStream.writeShort(signatureIndex); + dataOutputStream.writeShort(index); + } + + /** + * @return Constant pool used by this object. + */ + public ConstantPool getConstantPool() { + return constantPool; + } + + /** + * @return index of register where variable is stored + */ + public int getIndex() { + return index; + } + + /** + * @return Variable is valid within getStartPC() .. getStartPC()+getLength() + */ + public int getLength() { + return length; + } + + /** + * @return Variable name. + */ + public String getName() { + return constantPool.getConstantUtf8(nameIndex).getBytes(); + } + + /** + * @return Index in constant pool of variable name. + */ + public int getNameIndex() { + return nameIndex; + } + + /** + * @return index of register where variable was originally stored + */ + public int getOrigIndex() { + return origIndex; + } + + /** + * @return Signature. + */ + public String getSignature() { + return constantPool.getConstantUtf8(signatureIndex).getBytes(); + } + + /** + * @return Index in constant pool of variable signature. + */ + public int getSignatureIndex() { + return signatureIndex; + } + + /** + * @return Start of range where the variable is valid + */ + public int getStartPC() { + return startPc; + } + + /** + * @param constantPool Constant pool to be used for this object. + */ + public void setConstantPool(final ConstantPool constantPool) { + this.constantPool = constantPool; + } + + /** + * @param index the index in the local variable table of this variable + */ + public void setIndex(final int index) { // TODO unused + this.index = index; + } + + /** + * @param length the length of this local variable + */ + public void setLength(final int length) { + this.length = length; + } + + /** + * @param nameIndex the index into the constant pool for the name of this variable + */ + public void setNameIndex(final int nameIndex) { // TODO unused + this.nameIndex = nameIndex; + } + + /** + * @param signatureIndex the index into the constant pool for the signature of this variable + */ + public void setSignatureIndex(final int signatureIndex) { // TODO unused + this.signatureIndex = signatureIndex; + } + + /** + * @param startPc Specify range where the local variable is valid. + */ + public void setStartPC(final int startPc) { // TODO unused + this.startPc = startPc; + } + + /** + * @return string representation. + */ + @Override + public String toString() { + return toStringShared(false); + } + + /* + * Helper method shared with LocalVariableTypeTable + */ + String toStringShared(final boolean typeTable) { + final String name = getName(); + final String signature = Utility.signatureToString(getSignature(), false); + final String label = "LocalVariable" + (typeTable ? "Types" : ""); + return label + "(startPc = " + startPc + ", length = " + length + ", index = " + index + ":" + signature + " " + name + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/LocalVariableTable.java b/src/main/java/haidnor/jvm/bcel/classfile/LocalVariableTable.java new file mode 100644 index 0000000..a892a5e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/LocalVariableTable.java @@ -0,0 +1,191 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.stream.Stream; + +/** + * This class represents colection of local variables in a method. This attribute is contained in the Code + * attribute. + * + * @see Code + * @see LocalVariable + */ +public class LocalVariableTable extends Attribute implements Iterable { + + private LocalVariable[] localVariableTable; // variables + + /** + * Construct object from input stream. + * + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + LocalVariableTable(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (LocalVariable[]) null, constantPool); + final int localVariableTableLength = input.readUnsignedShort(); + localVariableTable = new LocalVariable[localVariableTableLength]; + for (int i = 0; i < localVariableTableLength; i++) { + localVariableTable[i] = new LocalVariable(input, constantPool); + } + } + + /** + * @param nameIndex Index in constant pool to 'LocalVariableTable' + * @param length Content length in bytes + * @param localVariableTable Table of local variables + * @param constantPool Array of constants + */ + public LocalVariableTable(final int nameIndex, final int length, final LocalVariable[] localVariableTable, final ConstantPool constantPool) { + super(Const.ATTR_LOCAL_VARIABLE_TABLE, nameIndex, length, constantPool); + this.localVariableTable = localVariableTable != null ? localVariableTable : LocalVariable.EMPTY_ARRAY; + Args.requireU2(this.localVariableTable.length, "localVariableTable.length"); + } + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a + * physical copy. + * + * @param c Source to copy. + */ + public LocalVariableTable(final LocalVariableTable c) { + this(c.getNameIndex(), c.getLength(), c.getLocalVariableTable(), c.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitLocalVariableTable(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final LocalVariableTable c = (LocalVariableTable) clone(); + c.localVariableTable = new LocalVariable[localVariableTable.length]; + Arrays.setAll(c.localVariableTable, i -> localVariableTable[i].copy()); + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump local variable table attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public final void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(localVariableTable.length); + for (final LocalVariable variable : localVariableTable) { + variable.dump(file); + } + } + + /** + * + * @param index the variable slot + * + * @return the first LocalVariable that matches the slot or null if not found + * + * @deprecated since 5.2 because multiple variables can share the same slot, use getLocalVariable(int index, int pc) + * instead. + */ + @java.lang.Deprecated + public final LocalVariable getLocalVariable(final int index) { + for (final LocalVariable variable : localVariableTable) { + if (variable.getIndex() == index) { + return variable; + } + } + return null; + } + + /** + * + * @param index the variable slot + * @param pc the current pc that this variable is alive + * + * @return the LocalVariable that matches or null if not found + */ + public final LocalVariable getLocalVariable(final int index, final int pc) { + for (final LocalVariable variable : localVariableTable) { + if (variable.getIndex() == index) { + final int startPc = variable.getStartPC(); + final int endPc = startPc + variable.getLength(); + if (pc >= startPc && pc <= endPc) { + return variable; + } + } + } + return null; + } + + /** + * @return Array of local variables of method. + */ + public final LocalVariable[] getLocalVariableTable() { + return localVariableTable; + } + + public final int getTableLength() { + return localVariableTable == null ? 0 : localVariableTable.length; + } + + @Override + public Iterator iterator() { + return Stream.of(localVariableTable).iterator(); + } + + public final void setLocalVariableTable(final LocalVariable[] localVariableTable) { + this.localVariableTable = localVariableTable; + } + + /** + * @return String representation. + */ + @Override + public final String toString() { + final StringBuilder buf = new StringBuilder(); + for (int i = 0; i < localVariableTable.length; i++) { + buf.append(localVariableTable[i]); + if (i < localVariableTable.length - 1) { + buf.append('\n'); + } + } + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/LocalVariableTypeTable.java b/src/main/java/haidnor/jvm/bcel/classfile/LocalVariableTypeTable.java new file mode 100644 index 0000000..9b92244 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/LocalVariableTypeTable.java @@ -0,0 +1,157 @@ +/** + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.stream.Stream; + +// The new table is used when generic types are about... + +//LocalVariableTable_attribute { +// u2 attribute_name_index; +// u4 attribute_length; +// u2 local_variable_table_length; +// { u2 start_pc; +// u2 length; +// u2 name_index; +// u2 descriptor_index; +// u2 index; +// } local_variable_table[local_variable_table_length]; +// } + +//LocalVariableTypeTable_attribute { +// u2 attribute_name_index; +// u4 attribute_length; +// u2 local_variable_type_table_length; +// { +// u2 start_pc; +// u2 length; +// u2 name_index; +// u2 signature_index; +// u2 index; +// } localVariableTypeTable[local_variable_type_table_length]; +// } +// J5TODO: Needs some testing ! + +/** + * @since 6.0 + */ +public class LocalVariableTypeTable extends Attribute implements Iterable { + + private LocalVariable[] localVariableTypeTable; // variables + + LocalVariableTypeTable(final int nameIdx, final int len, final DataInput input, final ConstantPool cpool) throws IOException { + this(nameIdx, len, (LocalVariable[]) null, cpool); + + final int localVariableTypeTableLength = input.readUnsignedShort(); + localVariableTypeTable = new LocalVariable[localVariableTypeTableLength]; + + for (int i = 0; i < localVariableTypeTableLength; i++) { + localVariableTypeTable[i] = new LocalVariable(input, cpool); + } + } + + public LocalVariableTypeTable(final int nameIndex, final int length, final LocalVariable[] localVariableTypeTable, final ConstantPool constantPool) { + super(Const.ATTR_LOCAL_VARIABLE_TYPE_TABLE, nameIndex, length, constantPool); + this.localVariableTypeTable = localVariableTypeTable != null ? localVariableTypeTable : LocalVariable.EMPTY_ARRAY; + Args.requireU2(this.localVariableTypeTable.length, "localVariableTypeTable.length"); + } + + public LocalVariableTypeTable(final LocalVariableTypeTable c) { + this(c.getNameIndex(), c.getLength(), c.getLocalVariableTypeTable(), c.getConstantPool()); + } + + @Override + public void accept(final Visitor v) { + v.visitLocalVariableTypeTable(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final LocalVariableTypeTable c = (LocalVariableTypeTable) clone(); + + c.localVariableTypeTable = new LocalVariable[localVariableTypeTable.length]; + Arrays.setAll(c.localVariableTypeTable, i -> localVariableTypeTable[i].copy()); + c.setConstantPool(constantPool); + return c; + } + + @Override + public final void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(localVariableTypeTable.length); + for (final LocalVariable variable : localVariableTypeTable) { + variable.dump(file); + } + } + + public final LocalVariable getLocalVariable(final int index) { + for (final LocalVariable variable : localVariableTypeTable) { + if (variable.getIndex() == index) { + return variable; + } + } + + return null; + } + + public final LocalVariable[] getLocalVariableTypeTable() { + return localVariableTypeTable; + } + + public final int getTableLength() { + return localVariableTypeTable == null ? 0 : localVariableTypeTable.length; + } + + @Override + public Iterator iterator() { + return Stream.of(localVariableTypeTable).iterator(); + } + + public final void setLocalVariableTable(final LocalVariable[] localVariableTable) { + this.localVariableTypeTable = localVariableTable; + } + + /** + * @return String representation. + */ + @Override + public final String toString() { + final StringBuilder buf = new StringBuilder(); + + for (int i = 0; i < localVariableTypeTable.length; i++) { + buf.append(localVariableTypeTable[i].toStringShared(true)); + + if (i < localVariableTypeTable.length - 1) { + buf.append('\n'); + } + } + + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/MethodParameter.java b/src/main/java/haidnor/jvm/bcel/classfile/MethodParameter.java new file mode 100644 index 0000000..0b0e9ca --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/MethodParameter.java @@ -0,0 +1,121 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Entry of the parameters table. + * + * @see The class File Format : + * The MethodParameters Attribute + * @since 6.0 + */ +public class MethodParameter implements Cloneable, Node { + + /** Index of the CONSTANT_Utf8_info structure in the constant_pool table representing the name of the parameter */ + private int nameIndex; + + /** The access flags */ + private int accessFlags; + + public MethodParameter() { + } + + /** + * Construct object from input stream. + * + * @param input Input stream + * @throws IOException if an I/O error occurs. + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + MethodParameter(final DataInput input) throws IOException { + nameIndex = input.readUnsignedShort(); + accessFlags = input.readUnsignedShort(); + } + + @Override + public void accept(final Visitor v) { + v.visitMethodParameter(this); + } + + /** + * @return deep copy of this object + */ + public MethodParameter copy() { + try { + return (MethodParameter) clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + /** + * Dump object to file stream on binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + public final void dump(final DataOutputStream file) throws IOException { + file.writeShort(nameIndex); + file.writeShort(accessFlags); + } + + public int getAccessFlags() { + return accessFlags; + } + + public int getNameIndex() { + return nameIndex; + } + + /** + * Returns the name of the parameter. + */ + public String getParameterName(final ConstantPool constantPool) { + if (nameIndex == 0) { + return null; + } + return constantPool.getConstantUtf8(nameIndex).getBytes(); + } + + public boolean isFinal() { + return (accessFlags & Const.ACC_FINAL) != 0; + } + + public boolean isMandated() { + return (accessFlags & Const.ACC_MANDATED) != 0; + } + + public boolean isSynthetic() { + return (accessFlags & Const.ACC_SYNTHETIC) != 0; + } + + public void setAccessFlags(final int accessFlags) { + this.accessFlags = accessFlags; + } + + public void setNameIndex(final int nameIndex) { + this.nameIndex = nameIndex; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/MethodParameters.java b/src/main/java/haidnor/jvm/bcel/classfile/MethodParameters.java new file mode 100644 index 0000000..86b6337 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/MethodParameters.java @@ -0,0 +1,97 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.stream.Stream; + +/** + * This class represents a MethodParameters attribute. + * + * @see The class File Format : + * The MethodParameters Attribute + * @since 6.0 + */ +public class MethodParameters extends Attribute implements Iterable { + + /** + * Empty array. + */ + private static final MethodParameter[] EMPTY_METHOD_PARAMETER_ARRAY = {}; + + private MethodParameter[] parameters = EMPTY_METHOD_PARAMETER_ARRAY; + + MethodParameters(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + super(Const.ATTR_METHOD_PARAMETERS, nameIndex, length, constantPool); + + final int parameterCount = input.readUnsignedByte(); + parameters = new MethodParameter[parameterCount]; + for (int i = 0; i < parameterCount; i++) { + parameters[i] = new MethodParameter(input); + } + } + + @Override + public void accept(final Visitor v) { + v.visitMethodParameters(this); + } + + @Override + public Attribute copy(final ConstantPool constantPool) { + final MethodParameters c = (MethodParameters) clone(); + c.parameters = new MethodParameter[parameters.length]; + + Arrays.setAll(c.parameters, i -> parameters[i].copy()); + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump method parameters attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeByte(parameters.length); + for (final MethodParameter parameter : parameters) { + parameter.dump(file); + } + } + + public MethodParameter[] getParameters() { + return parameters; + } + + @Override + public Iterator iterator() { + return Stream.of(parameters).iterator(); + } + + public void setParameters(final MethodParameter[] parameters) { + this.parameters = parameters; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Module.java b/src/main/java/haidnor/jvm/bcel/classfile/Module.java new file mode 100644 index 0000000..b4379ca --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Module.java @@ -0,0 +1,250 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * This class is derived from Attribute and represents the list of modules required, exported, opened or + * provided by a module. There may be at most one Module attribute in a ClassFile structure. + * + * @see Attribute + * @since 6.4.0 + */ +public final class Module extends Attribute { + + /** + * The module file name extension. + * + * @since 6.7.0 + */ + public static final String EXTENSION = ".jmod"; + + private final int moduleNameIndex; + private final int moduleFlags; + private final int moduleVersionIndex; + + private ModuleRequires[] requiresTable; + private ModuleExports[] exportsTable; + private ModuleOpens[] opensTable; + private final int usesCount; + private final int[] usesIndex; + private ModuleProvides[] providesTable; + + /** + * Construct object from input stream. + * + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + Module(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + super(Const.ATTR_MODULE, nameIndex, length, constantPool); + + moduleNameIndex = input.readUnsignedShort(); + moduleFlags = input.readUnsignedShort(); + moduleVersionIndex = input.readUnsignedShort(); + + final int requiresCount = input.readUnsignedShort(); + requiresTable = new ModuleRequires[requiresCount]; + for (int i = 0; i < requiresCount; i++) { + requiresTable[i] = new ModuleRequires(input); + } + + final int exportsCount = input.readUnsignedShort(); + exportsTable = new ModuleExports[exportsCount]; + for (int i = 0; i < exportsCount; i++) { + exportsTable[i] = new ModuleExports(input); + } + + final int opensCount = input.readUnsignedShort(); + opensTable = new ModuleOpens[opensCount]; + for (int i = 0; i < opensCount; i++) { + opensTable[i] = new ModuleOpens(input); + } + + usesCount = input.readUnsignedShort(); + usesIndex = new int[usesCount]; + for (int i = 0; i < usesCount; i++) { + usesIndex[i] = input.readUnsignedShort(); + } + + final int providesCount = input.readUnsignedShort(); + providesTable = new ModuleProvides[providesCount]; + for (int i = 0; i < providesCount; i++) { + providesTable[i] = new ModuleProvides(input); + } + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitModule(this); + } + + // TODO add more getters and setters? + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final Module c = (Module) clone(); + + c.requiresTable = new ModuleRequires[requiresTable.length]; + Arrays.setAll(c.requiresTable, i -> requiresTable[i].copy()); + + c.exportsTable = new ModuleExports[exportsTable.length]; + Arrays.setAll(c.exportsTable, i -> exportsTable[i].copy()); + + c.opensTable = new ModuleOpens[opensTable.length]; + Arrays.setAll(c.opensTable, i -> opensTable[i].copy()); + + c.providesTable = new ModuleProvides[providesTable.length]; + Arrays.setAll(c.providesTable, i -> providesTable[i].copy()); + + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump Module attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + + file.writeShort(moduleNameIndex); + file.writeShort(moduleFlags); + file.writeShort(moduleVersionIndex); + + file.writeShort(requiresTable.length); + for (final ModuleRequires entry : requiresTable) { + entry.dump(file); + } + + file.writeShort(exportsTable.length); + for (final ModuleExports entry : exportsTable) { + entry.dump(file); + } + + file.writeShort(opensTable.length); + for (final ModuleOpens entry : opensTable) { + entry.dump(file); + } + + file.writeShort(usesIndex.length); + for (final int entry : usesIndex) { + file.writeShort(entry); + } + + file.writeShort(providesTable.length); + for (final ModuleProvides entry : providesTable) { + entry.dump(file); + } + } + + /** + * @return table of exported interfaces + * @see ModuleExports + */ + public ModuleExports[] getExportsTable() { + return exportsTable; + } + + /** + * @return table of provided interfaces + * @see ModuleOpens + */ + public ModuleOpens[] getOpensTable() { + return opensTable; + } + + /** + * @return table of provided interfaces + * @see ModuleProvides + */ + public ModuleProvides[] getProvidesTable() { + return providesTable; + } + + /** + * @return table of required modules + * @see ModuleRequires + */ + public ModuleRequires[] getRequiresTable() { + return requiresTable; + } + + /** + * @return String representation, i.e., a list of packages. + */ + @Override + public String toString() { + final ConstantPool cp = super.getConstantPool(); + final StringBuilder buf = new StringBuilder(); + buf.append("Module:\n"); + buf.append(" name: ").append(Utility.pathToPackage(cp.getConstantString(moduleNameIndex, Const.CONSTANT_Module))).append("\n"); + buf.append(" flags: ").append(String.format("%04x", moduleFlags)).append("\n"); + final String version = moduleVersionIndex == 0 ? "0" : cp.getConstantString(moduleVersionIndex, Const.CONSTANT_Utf8); + buf.append(" version: ").append(version).append("\n"); + + buf.append(" requires(").append(requiresTable.length).append("):\n"); + for (final ModuleRequires module : requiresTable) { + buf.append(" ").append(module.toString(cp)).append("\n"); + } + + buf.append(" exports(").append(exportsTable.length).append("):\n"); + for (final ModuleExports module : exportsTable) { + buf.append(" ").append(module.toString(cp)).append("\n"); + } + + buf.append(" opens(").append(opensTable.length).append("):\n"); + for (final ModuleOpens module : opensTable) { + buf.append(" ").append(module.toString(cp)).append("\n"); + } + + buf.append(" uses(").append(usesIndex.length).append("):\n"); + for (final int index : usesIndex) { + final String className = cp.getConstantString(index, Const.CONSTANT_Class); + buf.append(" ").append(Utility.compactClassName(className, false)).append("\n"); + } + + buf.append(" provides(").append(providesTable.length).append("):\n"); + for (final ModuleProvides module : providesTable) { + buf.append(" ").append(module.toString(cp)).append("\n"); + } + + return buf.substring(0, buf.length() - 1); // remove the last newline + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ModuleExports.java b/src/main/java/haidnor/jvm/bcel/classfile/ModuleExports.java new file mode 100644 index 0000000..dfd307f --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ModuleExports.java @@ -0,0 +1,119 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class represents an entry in the exports table of the Module attribute. Each entry describes a package which may + * open the parent module. + * + * @see Module + * @since 6.4.0 + */ +public final class ModuleExports implements Cloneable, Node { + + private final int exportsIndex; // points to CONSTANT_Package_info + private final int exportsFlags; + private final int exportsToCount; + private final int[] exportsToIndex; // points to CONSTANT_Module_info + + /** + * Construct object from file stream. + * + * @param file Input stream + * @throws IOException if an I/O Exception occurs in readUnsignedShort + */ + ModuleExports(final DataInput file) throws IOException { + exportsIndex = file.readUnsignedShort(); + exportsFlags = file.readUnsignedShort(); + exportsToCount = file.readUnsignedShort(); + exportsToIndex = new int[exportsToCount]; + for (int i = 0; i < exportsToCount; i++) { + exportsToIndex[i] = file.readUnsignedShort(); + } + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitModuleExports(this); + } + + // TODO add more getters and setters? + + /** + * @return deep copy of this object + */ + public ModuleExports copy() { + try { + return (ModuleExports) clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + /** + * Dump table entry to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O Exception occurs in writeShort + */ + public void dump(final DataOutputStream file) throws IOException { + file.writeShort(exportsIndex); + file.writeShort(exportsFlags); + file.writeShort(exportsToCount); + for (final int entry : exportsToIndex) { + file.writeShort(entry); + } + } + + /** + * @return String representation + */ + @Override + public String toString() { + return "exports(" + exportsIndex + ", " + exportsFlags + ", " + exportsToCount + ", ...)"; + } + + /** + * @return Resolved string representation + */ + public String toString(final ConstantPool constantPool) { + final StringBuilder buf = new StringBuilder(); + final String packageName = constantPool.constantToString(exportsIndex, Const.CONSTANT_Package); + buf.append(Utility.compactClassName(packageName, false)); + buf.append(", ").append(String.format("%04x", exportsFlags)); + buf.append(", to(").append(exportsToCount).append("):\n"); + for (final int index : exportsToIndex) { + final String moduleName = constantPool.getConstantString(index, Const.CONSTANT_Module); + buf.append(" ").append(Utility.compactClassName(moduleName, false)).append("\n"); + } + return buf.substring(0, buf.length() - 1); // remove the last newline + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ModuleMainClass.java b/src/main/java/haidnor/jvm/bcel/classfile/ModuleMainClass.java new file mode 100644 index 0000000..37c3d8b --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ModuleMainClass.java @@ -0,0 +1,130 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from Attribute and indicates the main class of a module. There may be at most one + * ModuleMainClass attribute in a ClassFile structure. + * + * @see Attribute + */ +public final class ModuleMainClass extends Attribute { + + private int mainClassIndex; + + /** + * Construct object from input stream. + * + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + ModuleMainClass(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, 0, constantPool); + mainClassIndex = input.readUnsignedShort(); + } + + /** + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param mainClassIndex Host class index + * @param constantPool Array of constants + */ + public ModuleMainClass(final int nameIndex, final int length, final int mainClassIndex, final ConstantPool constantPool) { + super(Const.ATTR_NEST_MEMBERS, nameIndex, length, constantPool); + this.mainClassIndex = Args.requireU2(mainClassIndex, "mainClassIndex"); + } + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a + * physical copy. + * + * @param c Source to copy. + */ + public ModuleMainClass(final ModuleMainClass c) { + this(c.getNameIndex(), c.getLength(), c.getHostClassIndex(), c.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e., + * the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitModuleMainClass(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final ModuleMainClass c = (ModuleMainClass) clone(); + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump ModuleMainClass attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(mainClassIndex); + } + + /** + * @return index into constant pool of host class name. + */ + public int getHostClassIndex() { + return mainClassIndex; + } + + /** + * @param mainClassIndex the host class index + */ + public void setHostClassIndex(final int mainClassIndex) { + this.mainClassIndex = mainClassIndex; + } + + /** + * @return String representation + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + buf.append("ModuleMainClass: "); + final String className = super.getConstantPool().getConstantString(mainClassIndex, Const.CONSTANT_Class); + buf.append(Utility.compactClassName(className, false)); + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ModuleOpens.java b/src/main/java/haidnor/jvm/bcel/classfile/ModuleOpens.java new file mode 100644 index 0000000..e3a6411 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ModuleOpens.java @@ -0,0 +1,119 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class represents an entry in the opens table of the Module attribute. Each entry describes a package which the + * parent module opens. + * + * @see Module + * @since 6.4.0 + */ +public final class ModuleOpens implements Cloneable, Node { + + private final int opensIndex; // points to CONSTANT_Package_info + private final int opensFlags; + private final int opensToCount; + private final int[] opensToIndex; // points to CONSTANT_Module_info + + /** + * Construct object from file stream. + * + * @param file Input stream + * @throws IOException if an I/O Exception occurs in readUnsignedShort + */ + ModuleOpens(final DataInput file) throws IOException { + opensIndex = file.readUnsignedShort(); + opensFlags = file.readUnsignedShort(); + opensToCount = file.readUnsignedShort(); + opensToIndex = new int[opensToCount]; + for (int i = 0; i < opensToCount; i++) { + opensToIndex[i] = file.readUnsignedShort(); + } + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitModuleOpens(this); + } + + // TODO add more getters and setters? + + /** + * @return deep copy of this object + */ + public ModuleOpens copy() { + try { + return (ModuleOpens) clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + /** + * Dump table entry to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O Exception occurs in writeShort + */ + public void dump(final DataOutputStream file) throws IOException { + file.writeShort(opensIndex); + file.writeShort(opensFlags); + file.writeShort(opensToCount); + for (final int entry : opensToIndex) { + file.writeShort(entry); + } + } + + /** + * @return String representation + */ + @Override + public String toString() { + return "opens(" + opensIndex + ", " + opensFlags + ", " + opensToCount + ", ...)"; + } + + /** + * @return Resolved string representation + */ + public String toString(final ConstantPool constantPool) { + final StringBuilder buf = new StringBuilder(); + final String packageName = constantPool.constantToString(opensIndex, Const.CONSTANT_Package); + buf.append(Utility.compactClassName(packageName, false)); + buf.append(", ").append(String.format("%04x", opensFlags)); + buf.append(", to(").append(opensToCount).append("):\n"); + for (final int index : opensToIndex) { + final String moduleName = constantPool.getConstantString(index, Const.CONSTANT_Module); + buf.append(" ").append(Utility.compactClassName(moduleName, false)).append("\n"); + } + return buf.substring(0, buf.length() - 1); // remove the last newline + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ModulePackages.java b/src/main/java/haidnor/jvm/bcel/classfile/ModulePackages.java new file mode 100644 index 0000000..2bce0ba --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ModulePackages.java @@ -0,0 +1,163 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; +import org.apache.commons.lang3.ArrayUtils; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * This class is derived from Attribute and represents the list of packages that are exported or opened by the + * Module attribute. There may be at most one ModulePackages attribute in a ClassFile structure. + * + * @see Attribute + */ +public final class ModulePackages extends Attribute { + + private int[] packageIndexTable; + + /** + * Construct object from input stream. + * + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + ModulePackages(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (int[]) null, constantPool); + final int packageCount = input.readUnsignedShort(); + packageIndexTable = new int[packageCount]; + for (int i = 0; i < packageCount; i++) { + packageIndexTable[i] = input.readUnsignedShort(); + } + } + + /** + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param packageIndexTable Table of indices in constant pool + * @param constantPool Array of constants + */ + public ModulePackages(final int nameIndex, final int length, final int[] packageIndexTable, final ConstantPool constantPool) { + super(Const.ATTR_MODULE_PACKAGES, nameIndex, length, constantPool); + this.packageIndexTable = packageIndexTable != null ? packageIndexTable : ArrayUtils.EMPTY_INT_ARRAY; + Args.requireU2(this.packageIndexTable.length, "packageIndexTable.length"); + } + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a + * physical copy. + * + * @param c Source to copy. + */ + public ModulePackages(final ModulePackages c) { + this(c.getNameIndex(), c.getLength(), c.getPackageIndexTable(), c.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitModulePackages(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final ModulePackages c = (ModulePackages) clone(); + if (packageIndexTable != null) { + c.packageIndexTable = packageIndexTable.clone(); + } + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump ModulePackages attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(packageIndexTable.length); + for (final int index : packageIndexTable) { + file.writeShort(index); + } + } + + /** + * @return Length of package table. + */ + public int getNumberOfPackages() { + return packageIndexTable == null ? 0 : packageIndexTable.length; + } + + /** + * @return array of indices into constant pool of package names. + */ + public int[] getPackageIndexTable() { + return packageIndexTable; + } + + /** + * @return string array of package names + */ + public String[] getPackageNames() { + final String[] names = new String[packageIndexTable.length]; + Arrays.setAll(names, i -> Utility.pathToPackage(super.getConstantPool().getConstantString(packageIndexTable[i], Const.CONSTANT_Package))); + return names; + } + + /** + * @param packageIndexTable the list of package indexes Also redefines number_of_packages according to table length. + */ + public void setPackageIndexTable(final int[] packageIndexTable) { + this.packageIndexTable = packageIndexTable != null ? packageIndexTable : ArrayUtils.EMPTY_INT_ARRAY; + } + + /** + * @return String representation, i.e., a list of packages. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + buf.append("ModulePackages("); + buf.append(packageIndexTable.length); + buf.append("):\n"); + for (final int index : packageIndexTable) { + final String packageName = super.getConstantPool().getConstantString(index, Const.CONSTANT_Package); + buf.append(" ").append(Utility.compactClassName(packageName, false)).append("\n"); + } + return buf.substring(0, buf.length() - 1); // remove the last newline + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ModuleProvides.java b/src/main/java/haidnor/jvm/bcel/classfile/ModuleProvides.java new file mode 100644 index 0000000..b383725 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ModuleProvides.java @@ -0,0 +1,115 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class represents an entry in the provides table of the Module attribute. Each entry describes a service + * implementation that the parent module provides. + * + * @see Module + * @since 6.4.0 + */ +public final class ModuleProvides implements Cloneable, Node { + + private final int providesIndex; // points to CONSTANT_Class_info + private final int providesWithCount; + private final int[] providesWithIndex; // points to CONSTANT_Class_info + + /** + * Construct object from file stream. + * + * @param file Input stream + * @throws IOException if an I/O Exception occurs in readUnsignedShort + */ + ModuleProvides(final DataInput file) throws IOException { + providesIndex = file.readUnsignedShort(); + providesWithCount = file.readUnsignedShort(); + providesWithIndex = new int[providesWithCount]; + for (int i = 0; i < providesWithCount; i++) { + providesWithIndex[i] = file.readUnsignedShort(); + } + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitModuleProvides(this); + } + + // TODO add more getters and setters? + + /** + * @return deep copy of this object + */ + public ModuleProvides copy() { + try { + return (ModuleProvides) clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + /** + * Dump table entry to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O Exception occurs in writeShort + */ + public void dump(final DataOutputStream file) throws IOException { + file.writeShort(providesIndex); + file.writeShort(providesWithCount); + for (final int entry : providesWithIndex) { + file.writeShort(entry); + } + } + + /** + * @return String representation + */ + @Override + public String toString() { + return "provides(" + providesIndex + ", " + providesWithCount + ", ...)"; + } + + /** + * @return Resolved string representation + */ + public String toString(final ConstantPool constantPool) { + final StringBuilder buf = new StringBuilder(); + final String interfaceName = constantPool.constantToString(providesIndex, Const.CONSTANT_Class); + buf.append(Utility.compactClassName(interfaceName, false)); + buf.append(", with(").append(providesWithCount).append("):\n"); + for (final int index : providesWithIndex) { + final String className = constantPool.getConstantString(index, Const.CONSTANT_Class); + buf.append(" ").append(Utility.compactClassName(className, false)).append("\n"); + } + return buf.substring(0, buf.length() - 1); // remove the last newline + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ModuleRequires.java b/src/main/java/haidnor/jvm/bcel/classfile/ModuleRequires.java new file mode 100644 index 0000000..f91d9f7 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ModuleRequires.java @@ -0,0 +1,108 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class represents an entry in the requires table of the Module attribute. Each entry describes a module on which + * the parent module depends. + * + * @see Module + * @since 6.4.0 + */ +public final class ModuleRequires implements Cloneable, Node { + + private final int requiresIndex; // points to CONSTANT_Module_info + private final int requiresFlags; + private final int requiresVersionIndex; // either 0 or points to CONSTANT_Utf8_info + + /** + * Construct object from file stream. + * + * @param file Input stream + * @throws IOException if an I/O Exception occurs in readUnsignedShort + */ + ModuleRequires(final DataInput file) throws IOException { + requiresIndex = file.readUnsignedShort(); + requiresFlags = file.readUnsignedShort(); + requiresVersionIndex = file.readUnsignedShort(); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitModuleRequires(this); + } + + // TODO add more getters and setters? + + /** + * @return deep copy of this object + */ + public ModuleRequires copy() { + try { + return (ModuleRequires) clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + /** + * Dump table entry to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O Exception occurs in writeShort + */ + public void dump(final DataOutputStream file) throws IOException { + file.writeShort(requiresIndex); + file.writeShort(requiresFlags); + file.writeShort(requiresVersionIndex); + } + + /** + * @return String representation + */ + @Override + public String toString() { + return "requires(" + requiresIndex + ", " + String.format("%04x", requiresFlags) + ", " + requiresVersionIndex + ")"; + } + + /** + * @return Resolved string representation + */ + public String toString(final ConstantPool constantPool) { + final StringBuilder buf = new StringBuilder(); + final String moduleName = constantPool.constantToString(requiresIndex, Const.CONSTANT_Module); + buf.append(Utility.compactClassName(moduleName, false)); + buf.append(", ").append(String.format("%04x", requiresFlags)); + final String version = requiresVersionIndex == 0 ? "0" : constantPool.getConstantString(requiresVersionIndex, Const.CONSTANT_Utf8); + buf.append(", ").append(version); + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/NestHost.java b/src/main/java/haidnor/jvm/bcel/classfile/NestHost.java new file mode 100644 index 0000000..6f3e654 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/NestHost.java @@ -0,0 +1,130 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from Attribute and records the nest host of the nest to which the current class or + * interface claims to belong. There may be at most one NestHost attribute in a ClassFile structure. + * + * @see Attribute + */ +public final class NestHost extends Attribute { + + private int hostClassIndex; + + /** + * Constructs object from input stream. + * + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + NestHost(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, 0, constantPool); + hostClassIndex = input.readUnsignedShort(); + } + + /** + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param hostClassIndex Host class index + * @param constantPool Array of constants + */ + public NestHost(final int nameIndex, final int length, final int hostClassIndex, final ConstantPool constantPool) { + super(Const.ATTR_NEST_MEMBERS, nameIndex, length, constantPool); + this.hostClassIndex = Args.requireU2(hostClassIndex, "hostClassIndex"); + } + + /** + * Initializes from another object. Note that both objects use the same references (shallow copy). Use copy() for a + * physical copy. + * + * @param c Source to copy. + */ + public NestHost(final NestHost c) { + this(c.getNameIndex(), c.getLength(), c.getHostClassIndex(), c.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitNestHost(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final NestHost c = (NestHost) clone(); + c.setConstantPool(constantPool); + return c; + } + + /** + * Dumps NestHost attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(hostClassIndex); + } + + /** + * @return index into constant pool of host class name. + */ + public int getHostClassIndex() { + return hostClassIndex; + } + + /** + * @param hostClassIndex the host class index + */ + public void setHostClassIndex(final int hostClassIndex) { + this.hostClassIndex = hostClassIndex; + } + + /** + * @return String representation + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + buf.append("NestHost: "); + final String className = super.getConstantPool().getConstantString(hostClassIndex, Const.CONSTANT_Class); + buf.append(Utility.compactClassName(className, false)); + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/NestMembers.java b/src/main/java/haidnor/jvm/bcel/classfile/NestMembers.java new file mode 100644 index 0000000..1019688 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/NestMembers.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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; +import org.apache.commons.lang3.ArrayUtils; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * This class is derived from Attribute and records the classes and interfaces that are authorized to claim + * membership in the nest hosted by the current class or interface. There may be at most one NestMembers attribute in a + * ClassFile structure. + * + * @see Attribute + */ +public final class NestMembers extends Attribute { + + private int[] classes; + + /** + * Construct object from input stream. + * + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + NestMembers(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (int[]) null, constantPool); + final int classCount = input.readUnsignedShort(); + classes = new int[classCount]; + for (int i = 0; i < classCount; i++) { + classes[i] = input.readUnsignedShort(); + } + } + + /** + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param classes Table of indices in constant pool + * @param constantPool Array of constants + */ + public NestMembers(final int nameIndex, final int length, final int[] classes, final ConstantPool constantPool) { + super(Const.ATTR_NEST_MEMBERS, nameIndex, length, constantPool); + this.classes = classes != null ? classes : ArrayUtils.EMPTY_INT_ARRAY; + Args.requireU2(this.classes.length, "classes.length"); + } + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a + * physical copy. + * + * @param c Source to copy. + */ + public NestMembers(final NestMembers c) { + this(c.getNameIndex(), c.getLength(), c.getClasses(), c.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitNestMembers(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final NestMembers c = (NestMembers) clone(); + if (classes.length > 0) { + c.classes = classes.clone(); + } + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump NestMembers attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(classes.length); + for (final int index : classes) { + file.writeShort(index); + } + } + + /** + * @return array of indices into constant pool of class names. + */ + public int[] getClasses() { + return classes; + } + + /** + * @return string array of class names + */ + public String[] getClassNames() { + final String[] names = new String[classes.length]; + Arrays.setAll(names, i -> Utility.pathToPackage(super.getConstantPool().getConstantString(classes[i], Const.CONSTANT_Class))); + return names; + } + + /** + * @return Length of classes table. + */ + public int getNumberClasses() { + return classes.length; + } + + /** + * @param classes the list of class indexes Also redefines number_of_classes according to table length. + */ + public void setClasses(final int[] classes) { + this.classes = classes != null ? classes : ArrayUtils.EMPTY_INT_ARRAY; + } + + /** + * @return String representation, i.e., a list of classes. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + buf.append("NestMembers("); + buf.append(classes.length); + buf.append("):\n"); + for (final int index : classes) { + final String className = super.getConstantPool().getConstantString(index, Const.CONSTANT_Class); + buf.append(" ").append(Utility.compactClassName(className, false)).append("\n"); + } + return buf.substring(0, buf.length() - 1); // remove the last newline + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Node.java b/src/main/java/haidnor/jvm/bcel/classfile/Node.java new file mode 100644 index 0000000..d7c9445 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Node.java @@ -0,0 +1,25 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +/** + * Denote class to have an accept method(); + */ +public interface Node { + + void accept(Visitor obj); +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/PMGClass.java b/src/main/java/haidnor/jvm/bcel/classfile/PMGClass.java new file mode 100644 index 0000000..87ecb31 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/PMGClass.java @@ -0,0 +1,152 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from Attribute and represents a reference to a PMG attribute. + * + * @see Attribute + */ +public final class PMGClass extends Attribute { + + private int pmgClassIndex; + private int pmgIndex; + + /** + * Construct object from input stream. + * + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + PMGClass(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, input.readUnsignedShort(), input.readUnsignedShort(), constantPool); + } + + /** + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param pmgIndex index in constant pool for source file name + * @param pmgClassIndex Index in constant pool to CONSTANT_Utf8 + * @param constantPool Array of constants + */ + public PMGClass(final int nameIndex, final int length, final int pmgIndex, final int pmgClassIndex, final ConstantPool constantPool) { + super(Const.ATTR_PMG, nameIndex, length, constantPool); + this.pmgIndex = pmgIndex; + this.pmgClassIndex = pmgClassIndex; + } + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a + * physical copy. + * + * @param pgmClass Source to copy. + */ + public PMGClass(final PMGClass pgmClass) { + this(pgmClass.getNameIndex(), pgmClass.getLength(), pgmClass.getPMGIndex(), pgmClass.getPMGClassIndex(), pgmClass.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + println("Visiting non-standard PMGClass object"); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + return (Attribute) clone(); + } + + /** + * Dump source file attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(pmgIndex); + file.writeShort(pmgClassIndex); + } + + /** + * @return Index in constant pool of source file name. + */ + public int getPMGClassIndex() { + return pmgClassIndex; + } + + /** + * @return PMG class name. + */ + public String getPMGClassName() { + return super.getConstantPool().getConstantUtf8(pmgClassIndex).getBytes(); + } + + /** + * @return Index in constant pool of source file name. + */ + public int getPMGIndex() { + return pmgIndex; + } + + /** + * @return PMG name. + */ + public String getPMGName() { + return super.getConstantPool().getConstantUtf8(pmgIndex).getBytes(); + } + + /** + * @param pmgClassIndex + */ + public void setPMGClassIndex(final int pmgClassIndex) { + this.pmgClassIndex = pmgClassIndex; + } + + /** + * @param pmgIndex + */ + public void setPMGIndex(final int pmgIndex) { + this.pmgIndex = pmgIndex; + } + + /** + * @return String representation + */ + @Override + public String toString() { + return "PMGClass(" + getPMGName() + ", " + getPMGClassName() + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ParameterAnnotationEntry.java b/src/main/java/haidnor/jvm/bcel/classfile/ParameterAnnotationEntry.java new file mode 100644 index 0000000..99b1397 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ParameterAnnotationEntry.java @@ -0,0 +1,88 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * represents one parameter annotation in the parameter annotation table + * + * @since 6.0 + */ +public class ParameterAnnotationEntry implements Node { + + static final ParameterAnnotationEntry[] EMPTY_ARRAY = {}; + + public static ParameterAnnotationEntry[] createParameterAnnotationEntries(final Attribute[] attrs) { + // Find attributes that contain parameter annotation data + final List accumulatedAnnotations = new ArrayList<>(attrs.length); + for (final Attribute attribute : attrs) { + if (attribute instanceof ParameterAnnotations) { + final ParameterAnnotations runtimeAnnotations = (ParameterAnnotations) attribute; + Collections.addAll(accumulatedAnnotations, runtimeAnnotations.getParameterAnnotationEntries()); + } + } + return accumulatedAnnotations.toArray(ParameterAnnotationEntry.EMPTY_ARRAY); + } + + private final AnnotationEntry[] annotationTable; + + /** + * Construct object from input stream. + * + * @param input Input stream + * @throws IOException if an I/O error occurs. + */ + ParameterAnnotationEntry(final DataInput input, final ConstantPool constantPool) throws IOException { + final int annotationTableLength = input.readUnsignedShort(); + annotationTable = new AnnotationEntry[annotationTableLength]; + for (int i = 0; i < annotationTableLength; i++) { + // TODO isRuntimeVisible + annotationTable[i] = AnnotationEntry.read(input, constantPool, false); + } + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitParameterAnnotationEntry(this); + } + + public void dump(final DataOutputStream dos) throws IOException { + dos.writeShort(annotationTable.length); + for (final AnnotationEntry entry : annotationTable) { + entry.dump(dos); + } + } + + /** + * returns the array of annotation entries in this annotation + */ + public AnnotationEntry[] getAnnotationEntries() { + return annotationTable; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/ParameterAnnotations.java b/src/main/java/haidnor/jvm/bcel/classfile/ParameterAnnotations.java new file mode 100644 index 0000000..8b3071c --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/ParameterAnnotations.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 haidnor.jvm.bcel.classfile; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.stream.Stream; + +/** + * base class for parameter annotations + * + * @since 6.0 + */ +public abstract class ParameterAnnotations extends Attribute implements Iterable { + + /** Table of parameter annotations */ + private ParameterAnnotationEntry[] parameterAnnotationTable; + + /** + * @param parameterAnnotationType the subclass type of the parameter annotation + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + */ + ParameterAnnotations(final byte parameterAnnotationType, final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) + throws IOException { + this(parameterAnnotationType, nameIndex, length, (ParameterAnnotationEntry[]) null, constantPool); + final int numParameters = input.readUnsignedByte(); + parameterAnnotationTable = new ParameterAnnotationEntry[numParameters]; + for (int i = 0; i < numParameters; i++) { + parameterAnnotationTable[i] = new ParameterAnnotationEntry(input, constantPool); + } + } + + /** + * @param parameterAnnotationType the subclass type of the parameter annotation + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param parameterAnnotationTable the actual parameter annotations + * @param constantPool Array of constants + */ + public ParameterAnnotations(final byte parameterAnnotationType, final int nameIndex, final int length, + final ParameterAnnotationEntry[] parameterAnnotationTable, final ConstantPool constantPool) { + super(parameterAnnotationType, nameIndex, length, constantPool); + this.parameterAnnotationTable = parameterAnnotationTable; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitParameterAnnotation(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + return (Attribute) clone(); + } + + @Override + public void dump(final DataOutputStream dos) throws IOException { + super.dump(dos); + dos.writeByte(parameterAnnotationTable.length); + + for (final ParameterAnnotationEntry element : parameterAnnotationTable) { + element.dump(dos); + } + + } + + /** + * returns the array of parameter annotation entries in this parameter annotation + */ + public ParameterAnnotationEntry[] getParameterAnnotationEntries() { + return parameterAnnotationTable; + } + + /** + * @return the parameter annotation entry table + */ + public final ParameterAnnotationEntry[] getParameterAnnotationTable() { + return parameterAnnotationTable; + } + + @Override + public Iterator iterator() { + return Stream.of(parameterAnnotationTable).iterator(); + } + + /** + * @param parameterAnnotationTable the entries to set in this parameter annotation + */ + public final void setParameterAnnotationTable(final ParameterAnnotationEntry[] parameterAnnotationTable) { + this.parameterAnnotationTable = parameterAnnotationTable; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/RuntimeInvisibleAnnotations.java b/src/main/java/haidnor/jvm/bcel/classfile/RuntimeInvisibleAnnotations.java new file mode 100644 index 0000000..4120d07 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/RuntimeInvisibleAnnotations.java @@ -0,0 +1,56 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * represents an annotation that is represented in the class file but is not provided to the JVM. + * + * @since 6.0 + */ +public class RuntimeInvisibleAnnotations extends Annotations { + + /** + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException Thrown when an I/O exception of some sort has occurred. + */ + public RuntimeInvisibleAnnotations(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + super(Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS, nameIndex, length, input, constantPool, false); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + return (Attribute) clone(); + } + + @Override + public final void dump(final DataOutputStream dos) throws IOException { + super.dump(dos); + writeAnnotations(dos); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/RuntimeInvisibleParameterAnnotations.java b/src/main/java/haidnor/jvm/bcel/classfile/RuntimeInvisibleParameterAnnotations.java new file mode 100644 index 0000000..cd799b5 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/RuntimeInvisibleParameterAnnotations.java @@ -0,0 +1,42 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.IOException; + +/** + * Represents a parameter annotation that is represented in the class file but is not provided to the JVM. + * + * @since 6.0 + */ +public class RuntimeInvisibleParameterAnnotations extends ParameterAnnotations { + + /** + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException Thrown when an I/O exception of some sort has occurred. + */ + public RuntimeInvisibleParameterAnnotations(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) + throws IOException { + super(Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, nameIndex, length, input, constantPool); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/RuntimeVisibleAnnotations.java b/src/main/java/haidnor/jvm/bcel/classfile/RuntimeVisibleAnnotations.java new file mode 100644 index 0000000..b523905 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/RuntimeVisibleAnnotations.java @@ -0,0 +1,56 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * represents an annotation that is represented in the class file and is provided to the JVM. + * + * @since 6.0 + */ +public class RuntimeVisibleAnnotations extends Annotations { + + /** + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException Thrown when an I/O exception of some sort has occurred. + */ + public RuntimeVisibleAnnotations(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + super(Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS, nameIndex, length, input, constantPool, true); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + return (Attribute) clone(); + } + + @Override + public final void dump(final DataOutputStream dos) throws IOException { + super.dump(dos); + writeAnnotations(dos); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/RuntimeVisibleParameterAnnotations.java b/src/main/java/haidnor/jvm/bcel/classfile/RuntimeVisibleParameterAnnotations.java new file mode 100644 index 0000000..2e311cd --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/RuntimeVisibleParameterAnnotations.java @@ -0,0 +1,42 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.IOException; + +/** + * Represents a parameter annotation that is represented in the class file and is provided to the JVM. + * + * @since 6.0 + */ +public class RuntimeVisibleParameterAnnotations extends ParameterAnnotations { + + /** + * @param nameIndex Index pointing to the name Code + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException Thrown when an I/O exception of some sort has occurred. + */ + public RuntimeVisibleParameterAnnotations(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) + throws IOException { + super(Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, nameIndex, length, input, constantPool); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Signature.java b/src/main/java/haidnor/jvm/bcel/classfile/Signature.java new file mode 100644 index 0000000..ca0b000 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Signature.java @@ -0,0 +1,253 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.ByteArrayInputStream; +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * This class is derived from Attribute and represents a reference to a GJ attribute. + * + * @see Attribute + */ +public final class Signature extends Attribute { + + /** + * Extends ByteArrayInputStream to make 'unreading' chars possible. + */ + private static final class MyByteArrayInputStream extends ByteArrayInputStream { + + MyByteArrayInputStream(final String data) { + super(data.getBytes(StandardCharsets.UTF_8)); + } + + String getData() { + return new String(buf, StandardCharsets.UTF_8); + } + + void unread() { + if (pos > 0) { + pos--; + } + } + } + + private static boolean identStart(final int ch) { + return ch == 'T' || ch == 'L'; + } + + // @since 6.0 is no longer final + public static boolean isActualParameterList(final String s) { + return s.startsWith("L") && s.endsWith(">;"); + } + + // @since 6.0 is no longer final + public static boolean isFormalParameterList(final String s) { + return s.startsWith("<") && s.indexOf(':') > 0; + } + + private static void matchGJIdent(final MyByteArrayInputStream in, final StringBuilder buf) { + int ch; + matchIdent(in, buf); + ch = in.read(); + if (ch == '<' || ch == '(') { // Parameterized or method + // System.out.println("Enter <"); + buf.append((char) ch); + matchGJIdent(in, buf); + while ((ch = in.read()) != '>' && ch != ')') { // List of parameters + if (ch == -1) { + throw new IllegalArgumentException("Illegal signature: " + in.getData() + " reaching EOF"); + } + // System.out.println("Still no >"); + buf.append(", "); + in.unread(); + matchGJIdent(in, buf); // Recursive call + } + // System.out.println("Exit >"); + buf.append((char) ch); + } else { + in.unread(); + } + ch = in.read(); + if (identStart(ch)) { + in.unread(); + matchGJIdent(in, buf); + } else if (ch == ')') { + in.unread(); + } else if (ch != ';') { + throw new IllegalArgumentException("Illegal signature: " + in.getData() + " read " + (char) ch); + } + } + + private static void matchIdent(final MyByteArrayInputStream in, final StringBuilder buf) { + int ch; + if ((ch = in.read()) == -1) { + throw new IllegalArgumentException("Illegal signature: " + in.getData() + " no ident, reaching EOF"); + } + // System.out.println("return from ident:" + (char)ch); + if (!identStart(ch)) { + final StringBuilder buf2 = new StringBuilder(); + int count = 1; + while (Character.isJavaIdentifierPart((char) ch)) { + buf2.append((char) ch); + count++; + ch = in.read(); + } + if (ch == ':') { // Ok, formal parameter + final int skipExpected = "Ljava/lang/Object".length(); + final long skipActual = in.skip(skipExpected); + if (skipActual != skipExpected) { + throw new IllegalStateException(String.format("Unexpected skip: expected=%,d, actual=%,d", skipExpected, skipActual)); + } + buf.append(buf2); + ch = in.read(); + in.unread(); + // System.out.println("so far:" + buf2 + ":next:" +(char)ch); + } else { + for (int i = 0; i < count; i++) { + in.unread(); + } + } + return; + } + final StringBuilder buf2 = new StringBuilder(); + ch = in.read(); + do { + buf2.append((char) ch); + ch = in.read(); + // System.out.println("within ident:"+ (char)ch); + } while (ch != -1 && (Character.isJavaIdentifierPart((char) ch) || ch == '/')); + buf.append(Utility.pathToPackage(buf2.toString())); + // System.out.println("regular return ident:"+ (char)ch + ":" + buf2); + if (ch != -1) { + in.unread(); + } + } + + public static String translate(final String s) { + // System.out.println("Sig:" + s); + final StringBuilder buf = new StringBuilder(); + matchGJIdent(new MyByteArrayInputStream(s), buf); + return buf.toString(); + } + + private int signatureIndex; + + /** + * Construct object from file stream. + * + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + Signature(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, input.readUnsignedShort(), constantPool); + } + + /** + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param signatureIndex Index in constant pool to CONSTANT_Utf8 + * @param constantPool Array of constants + */ + public Signature(final int nameIndex, final int length, final int signatureIndex, final ConstantPool constantPool) { + super(Const.ATTR_SIGNATURE, nameIndex, Args.require(length, 2, "Signature length attribute"), constantPool); + this.signatureIndex = signatureIndex; + // validate: + Objects.requireNonNull(constantPool.getConstantUtf8(signatureIndex), "constantPool.getConstantUtf8(signatureIndex)"); + } + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a + * physical copy. + * + * @param c Source to copy. + */ + public Signature(final Signature c) { + this(c.getNameIndex(), c.getLength(), c.getSignatureIndex(), c.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + // System.err.println("Visiting non-standard Signature object"); + v.visitSignature(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + return (Attribute) clone(); + } + + /** + * Dump source file attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(signatureIndex); + } + + /** + * @return GJ signature. + */ + public String getSignature() { + return super.getConstantPool().getConstantUtf8(signatureIndex).getBytes(); + } + + /** + * @return Index in constant pool of source file name. + */ + public int getSignatureIndex() { + return signatureIndex; + } + + /** + * @param signatureIndex the index info the constant pool of this signature + */ + public void setSignatureIndex(final int signatureIndex) { + this.signatureIndex = signatureIndex; + } + + /** + * @return String representation + */ + @Override + public String toString() { + return "Signature: " + getSignature(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/SimpleElementValue.java b/src/main/java/haidnor/jvm/bcel/classfile/SimpleElementValue.java new file mode 100644 index 0000000..892c35b --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/SimpleElementValue.java @@ -0,0 +1,178 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * @since 6.0 + */ +public class SimpleElementValue extends ElementValue { + private int index; + + public SimpleElementValue(final int type, final int index, final ConstantPool cpool) { + super(type, cpool); + this.index = index; + } + + @Override + public void dump(final DataOutputStream dos) throws IOException { + final int type = super.getType(); + dos.writeByte(type); // u1 kind of value + switch (type) { + case PRIMITIVE_INT: + case PRIMITIVE_BYTE: + case PRIMITIVE_CHAR: + case PRIMITIVE_FLOAT: + case PRIMITIVE_LONG: + case PRIMITIVE_BOOLEAN: + case PRIMITIVE_SHORT: + case PRIMITIVE_DOUBLE: + case STRING: + dos.writeShort(getIndex()); + break; + default: + throw new ClassFormatException("SimpleElementValue doesnt know how to write out type " + type); + } + } + + /** + * @return Value entry index in the cpool + */ + public int getIndex() { + return index; + } + + public boolean getValueBoolean() { + if (super.getType() != PRIMITIVE_BOOLEAN) { + throw new IllegalStateException("Dont call getValueBoolean() on a non BOOLEAN ElementValue"); + } + final ConstantInteger bo = (ConstantInteger) super.getConstantPool().getConstant(getIndex()); + return bo.getBytes() != 0; + } + + public byte getValueByte() { + if (super.getType() != PRIMITIVE_BYTE) { + throw new IllegalStateException("Dont call getValueByte() on a non BYTE ElementValue"); + } + return (byte) super.getConstantPool().getConstantInteger(getIndex()).getBytes(); + } + + public char getValueChar() { + if (super.getType() != PRIMITIVE_CHAR) { + throw new IllegalStateException("Dont call getValueChar() on a non CHAR ElementValue"); + } + return (char) super.getConstantPool().getConstantInteger(getIndex()).getBytes(); + } + + public double getValueDouble() { + if (super.getType() != PRIMITIVE_DOUBLE) { + throw new IllegalStateException("Dont call getValueDouble() on a non DOUBLE ElementValue"); + } + final ConstantDouble d = (ConstantDouble) super.getConstantPool().getConstant(getIndex()); + return d.getBytes(); + } + + public float getValueFloat() { + if (super.getType() != PRIMITIVE_FLOAT) { + throw new IllegalStateException("Dont call getValueFloat() on a non FLOAT ElementValue"); + } + final ConstantFloat f = (ConstantFloat) super.getConstantPool().getConstant(getIndex()); + return f.getBytes(); + } + + public int getValueInt() { + if (super.getType() != PRIMITIVE_INT) { + throw new IllegalStateException("Dont call getValueInt() on a non INT ElementValue"); + } + return super.getConstantPool().getConstantInteger(getIndex()).getBytes(); + } + + public long getValueLong() { + if (super.getType() != PRIMITIVE_LONG) { + throw new IllegalStateException("Dont call getValueLong() on a non LONG ElementValue"); + } + final ConstantLong j = (ConstantLong) super.getConstantPool().getConstant(getIndex()); + return j.getBytes(); + } + + public short getValueShort() { + if (super.getType() != PRIMITIVE_SHORT) { + throw new IllegalStateException("Dont call getValueShort() on a non SHORT ElementValue"); + } + final ConstantInteger s = (ConstantInteger) super.getConstantPool().getConstant(getIndex()); + return (short) s.getBytes(); + } + + public String getValueString() { + if (super.getType() != STRING) { + throw new IllegalStateException("Dont call getValueString() on a non STRING ElementValue"); + } + return super.getConstantPool().getConstantUtf8(getIndex()).getBytes(); + } + + public void setIndex(final int index) { + this.index = index; + } + + // Whatever kind of value it is, return it as a string + @Override + public String stringifyValue() { + final ConstantPool cpool = super.getConstantPool(); + final int type = super.getType(); + switch (type) { + case PRIMITIVE_INT: + return Integer.toString(cpool.getConstantInteger(getIndex()).getBytes()); + case PRIMITIVE_LONG: + final ConstantLong j = cpool.getConstant(getIndex(), Const.CONSTANT_Long, ConstantLong.class); + return Long.toString(j.getBytes()); + case PRIMITIVE_DOUBLE: + final ConstantDouble d = cpool.getConstant(getIndex(), Const.CONSTANT_Double, ConstantDouble.class); + return Double.toString(d.getBytes()); + case PRIMITIVE_FLOAT: + final ConstantFloat f = cpool.getConstant(getIndex(), Const.CONSTANT_Float, ConstantFloat.class); + return Float.toString(f.getBytes()); + case PRIMITIVE_SHORT: + final ConstantInteger s = cpool.getConstantInteger(getIndex()); + return Integer.toString(s.getBytes()); + case PRIMITIVE_BYTE: + final ConstantInteger b = cpool.getConstantInteger(getIndex()); + return Integer.toString(b.getBytes()); + case PRIMITIVE_CHAR: + final ConstantInteger ch = cpool.getConstantInteger(getIndex()); + return String.valueOf((char) ch.getBytes()); + case PRIMITIVE_BOOLEAN: + final ConstantInteger bo = cpool.getConstantInteger(getIndex()); + if (bo.getBytes() == 0) { + return "false"; + } + return "true"; + case STRING: + return cpool.getConstantUtf8(getIndex()).getBytes(); + default: + throw new IllegalStateException("SimpleElementValue class does not know how to stringify type " + type); + } + } + + @Override + public String toString() { + return stringifyValue(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/SourceFile.java b/src/main/java/haidnor/jvm/bcel/classfile/SourceFile.java new file mode 100644 index 0000000..c61f9ed --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/SourceFile.java @@ -0,0 +1,133 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from Attribute and represents a reference to the source file of this class. At most + * one SourceFile attribute should appear per classfile. The intention of this class is that it is instantiated from the + * Attribute.readAttribute() method. + * + * @see Attribute + */ +public final class SourceFile extends Attribute { + + private int sourceFileIndex; + + /** + * Construct object from input stream. + * + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + SourceFile(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, input.readUnsignedShort(), constantPool); + } + + /** + * @param nameIndex Index in constant pool to CONSTANT_Utf8, which should represent the string "SourceFile". + * @param length Content length in bytes, the value should be 2. + * @param constantPool The constant pool that this attribute is associated with. + * @param sourceFileIndex Index in constant pool to CONSTANT_Utf8. This string will be interpreted as the name of the + * file from which this class was compiled. It will not be interpreted as indicating the name of the directory + * contqining the file or an absolute path; this information has to be supplied the consumer of this attribute - + * in many cases, the JVM. + */ + public SourceFile(final int nameIndex, final int length, final int sourceFileIndex, final ConstantPool constantPool) { + super(Const.ATTR_SOURCE_FILE, nameIndex, Args.require(length, 2, "SourceFile length attribute"), constantPool); + this.sourceFileIndex = Args.requireU2(sourceFileIndex, 0, constantPool.getLength(), "SourceFile source file index"); + } + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a + * physical copy. + * + * @param c Source to copy. + */ + public SourceFile(final SourceFile c) { + this(c.getNameIndex(), c.getLength(), c.getSourceFileIndex(), c.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitSourceFile(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + return (Attribute) clone(); + } + + /** + * Dump source file attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(sourceFileIndex); + } + + /** + * @return Index in constant pool of source file name. + */ + public int getSourceFileIndex() { + return sourceFileIndex; + } + + /** + * @return Source file name. + */ + public String getSourceFileName() { + return super.getConstantPool().getConstantUtf8(sourceFileIndex).getBytes(); + } + + /** + * @param sourceFileIndex + */ + public void setSourceFileIndex(final int sourceFileIndex) { + this.sourceFileIndex = sourceFileIndex; + } + + /** + * @return String representation + */ + @Override + public String toString() { + return "SourceFile: " + getSourceFileName(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/StackMap.java b/src/main/java/haidnor/jvm/bcel/classfile/StackMap.java new file mode 100644 index 0000000..543db9f --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/StackMap.java @@ -0,0 +1,161 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * This class represents a stack map attribute used for preverification of Java classes for the + * Java 2 Micro Edition (J2ME). This attribute is used by the + * KVM and contained within the Code attribute of a method. See CLDC + * specification �5.3.1.2 + * + *
+ * StackMapTable_attribute {
+ *   u2              attribute_name_index;
+ *   u4              attribute_length;
+ *   u2              number_of_entries;
+ *   stack_map_frame entries[number_of_entries];
+ * }
+ * 
+ * + * @see Code + * @see StackMapEntry + * @see StackMapType + */ +public final class StackMap extends Attribute { + + private StackMapEntry[] table; // Table of stack map entries + + /** + * Construct object from input stream. + * + * @param nameIndex Index of name + * @param length Content length in bytes + * @param dataInput Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + StackMap(final int nameIndex, final int length, final DataInput dataInput, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (StackMapEntry[]) null, constantPool); + final int mapLength = dataInput.readUnsignedShort(); + table = new StackMapEntry[mapLength]; + for (int i = 0; i < mapLength; i++) { + table[i] = new StackMapEntry(dataInput, constantPool); + } + } + + /* + * @param nameIndex Index of name + * + * @param length Content length in bytes + * + * @param map Table of stack map entries + * + * @param constantPool Array of constants + */ + public StackMap(final int nameIndex, final int length, final StackMapEntry[] table, final ConstantPool constantPool) { + super(Const.ATTR_STACK_MAP, nameIndex, length, constantPool); + this.table = table != null ? table : StackMapEntry.EMPTY_ARRAY; + Args.requireU2(this.table.length, "table.length"); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackMap(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final StackMap c = (StackMap) clone(); + c.table = new StackMapEntry[table.length]; + Arrays.setAll(c.table, i -> table[i].copy()); + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump stack map table attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(table.length); + for (final StackMapEntry entry : table) { + entry.dump(file); + } + } + + public int getMapLength() { + return table.length; + } + + /** + * @return Array of stack map entries + */ + public StackMapEntry[] getStackMap() { + return table; + } + + /** + * @param table Array of stack map entries + */ + public void setStackMap(final StackMapEntry[] table) { + this.table = table != null ? table : StackMapEntry.EMPTY_ARRAY; + int len = 2; // Length of 'number_of_entries' field prior to the array of stack maps + for (final StackMapEntry element : this.table) { + len += element.getMapEntrySize(); + } + setLength(len); + } + + /** + * @return String representation. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder("StackMap("); + int runningOffset = -1; // no +1 on first entry + for (int i = 0; i < table.length; i++) { + runningOffset = table[i].getByteCodeOffset() + runningOffset + 1; + buf.append(String.format("%n@%03d %s", runningOffset, table[i])); + if (i < table.length - 1) { + buf.append(", "); + } + } + buf.append(')'); + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/StackMapEntry.java b/src/main/java/haidnor/jvm/bcel/classfile/StackMapEntry.java new file mode 100644 index 0000000..58413c7 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/StackMapEntry.java @@ -0,0 +1,415 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * This class represents a stack map entry recording the types of local variables and the of stack items at a given + * byte code offset. See CLDC specification 5.3.1.2. + * + * See also https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.4 + * + *
+ * union stack_map_frame {
+ *   same_frame;
+ *   same_locals_1_stack_item_frame;
+ *   same_locals_1_stack_item_frame_extended;
+ *   chop_frame;
+ *   same_frame_extended;
+ *   append_frame;
+ *   full_frame;
+ * }
+ * 
+ * @see StackMap + * @see StackMapType + */ +public final class StackMapEntry implements Node, Cloneable { + + static final StackMapEntry[] EMPTY_ARRAY = {}; + + private int frameType; + private int byteCodeOffset; + private StackMapType[] typesOfLocals; + private StackMapType[] typesOfStackItems; + private ConstantPool constantPool; + + /** + * Construct object from input stream. + * + * @param dataInput Input stream + * @throws IOException if an I/O error occurs. + */ + StackMapEntry(final DataInput dataInput, final ConstantPool constantPool) throws IOException { + this(dataInput.readByte() & 0xFF, -1, null, null, constantPool); + + if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) { + byteCodeOffset = frameType - Const.SAME_FRAME; + } else if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { + byteCodeOffset = frameType - Const.SAME_LOCALS_1_STACK_ITEM_FRAME; + typesOfStackItems = new StackMapType[] { new StackMapType(dataInput, constantPool) }; + } else if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { + byteCodeOffset = dataInput.readUnsignedShort(); + typesOfStackItems = new StackMapType[] { new StackMapType(dataInput, constantPool) }; + } else if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX) { + byteCodeOffset = dataInput.readUnsignedShort(); + } else if (frameType == Const.SAME_FRAME_EXTENDED) { + byteCodeOffset = dataInput.readUnsignedShort(); + } else if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) { + byteCodeOffset = dataInput.readUnsignedShort(); + final int numberOfLocals = frameType - 251; + typesOfLocals = new StackMapType[numberOfLocals]; + for (int i = 0; i < numberOfLocals; i++) { + typesOfLocals[i] = new StackMapType(dataInput, constantPool); + } + } else if (frameType == Const.FULL_FRAME) { + byteCodeOffset = dataInput.readUnsignedShort(); + final int numberOfLocals = dataInput.readUnsignedShort(); + typesOfLocals = new StackMapType[numberOfLocals]; + for (int i = 0; i < numberOfLocals; i++) { + typesOfLocals[i] = new StackMapType(dataInput, constantPool); + } + final int numberOfStackItems = dataInput.readUnsignedShort(); + typesOfStackItems = new StackMapType[numberOfStackItems]; + for (int i = 0; i < numberOfStackItems; i++) { + typesOfStackItems[i] = new StackMapType(dataInput, constantPool); + } + } else { + /* Can't happen */ + throw new ClassFormatException("Invalid frame type found while parsing stack map table: " + frameType); + } + } + + /** + * DO NOT USE + * + * @param byteCodeOffset + * @param numberOfLocals NOT USED + * @param typesOfLocals array of {@link StackMapType}s of locals + * @param numberOfStackItems NOT USED + * @param typesOfStackItems array ot {@link StackMapType}s of stack items + * @param constantPool the constant pool + * @deprecated Since 6.0, use {@link #StackMapEntry(int, int, StackMapType[], StackMapType[], ConstantPool)} instead + */ + @java.lang.Deprecated + public StackMapEntry(final int byteCodeOffset, final int numberOfLocals, final StackMapType[] typesOfLocals, final int numberOfStackItems, + final StackMapType[] typesOfStackItems, final ConstantPool constantPool) { + this.byteCodeOffset = byteCodeOffset; + this.typesOfLocals = typesOfLocals != null ? typesOfLocals : StackMapType.EMPTY_ARRAY; + this.typesOfStackItems = typesOfStackItems != null ? typesOfStackItems : StackMapType.EMPTY_ARRAY; + this.constantPool = constantPool; + if (numberOfLocals < 0) { + throw new IllegalArgumentException("numberOfLocals < 0"); + } + if (numberOfStackItems < 0) { + throw new IllegalArgumentException("numberOfStackItems < 0"); + } + } + + /** + * Create an instance + * + * @param tag the frameType to use + * @param byteCodeOffset + * @param typesOfLocals array of {@link StackMapType}s of locals + * @param typesOfStackItems array ot {@link StackMapType}s of stack items + * @param constantPool the constant pool + */ + public StackMapEntry(final int tag, final int byteCodeOffset, final StackMapType[] typesOfLocals, final StackMapType[] typesOfStackItems, + final ConstantPool constantPool) { + this.frameType = tag; + this.byteCodeOffset = byteCodeOffset; + this.typesOfLocals = typesOfLocals != null ? typesOfLocals : StackMapType.EMPTY_ARRAY; + this.typesOfStackItems = typesOfStackItems != null ? typesOfStackItems : StackMapType.EMPTY_ARRAY; + this.constantPool = constantPool; + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackMapEntry(this); + } + + /** + * @return deep copy of this object + */ + public StackMapEntry copy() { + StackMapEntry e; + try { + e = (StackMapEntry) clone(); + } catch (final CloneNotSupportedException ex) { + throw new Error("Clone Not Supported"); + } + + e.typesOfLocals = new StackMapType[typesOfLocals.length]; + Arrays.setAll(e.typesOfLocals, i -> typesOfLocals[i].copy()); + e.typesOfStackItems = new StackMapType[typesOfStackItems.length]; + Arrays.setAll(e.typesOfStackItems, i -> typesOfStackItems[i].copy()); + return e; + } + + /** + * Dump stack map entry + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + public void dump(final DataOutputStream file) throws IOException { + file.write(frameType); + if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { + typesOfStackItems[0].dump(file); + } else if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { + file.writeShort(byteCodeOffset); + typesOfStackItems[0].dump(file); + } else if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX) { + file.writeShort(byteCodeOffset); + } else if (frameType == Const.SAME_FRAME_EXTENDED) { + file.writeShort(byteCodeOffset); + } else if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) { + file.writeShort(byteCodeOffset); + for (final StackMapType type : typesOfLocals) { + type.dump(file); + } + } else if (frameType == Const.FULL_FRAME) { + file.writeShort(byteCodeOffset); + file.writeShort(typesOfLocals.length); + for (final StackMapType type : typesOfLocals) { + type.dump(file); + } + file.writeShort(typesOfStackItems.length); + for (final StackMapType type : typesOfStackItems) { + type.dump(file); + } + } else if (!(frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX)) { + /* Can't happen */ + throw new ClassFormatException("Invalid Stack map table tag: " + frameType); + } + } + + public int getByteCodeOffset() { + return byteCodeOffset; + } + + /** + * @return Constant pool used by this object. + */ + public ConstantPool getConstantPool() { + return constantPool; + } + + public int getFrameType() { + return frameType; + } + + /** + * Calculate stack map entry size + * + */ + int getMapEntrySize() { + if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) { + return 1; + } + if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { + return 1 + (typesOfStackItems[0].hasIndex() ? 3 : 1); + } + if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { + return 3 + (typesOfStackItems[0].hasIndex() ? 3 : 1); + } + if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX || frameType == Const.SAME_FRAME_EXTENDED) { + return 3; + } + if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) { + int len = 3; + for (final StackMapType typesOfLocal : typesOfLocals) { + len += typesOfLocal.hasIndex() ? 3 : 1; + } + return len; + } + if (frameType != Const.FULL_FRAME) { + throw new IllegalStateException("Invalid StackMap frameType: " + frameType); + } + int len = 7; + for (final StackMapType typesOfLocal : typesOfLocals) { + len += typesOfLocal.hasIndex() ? 3 : 1; + } + for (final StackMapType typesOfStackItem : typesOfStackItems) { + len += typesOfStackItem.hasIndex() ? 3 : 1; + } + return len; + } + + public int getNumberOfLocals() { + return typesOfLocals.length; + } + + public int getNumberOfStackItems() { + return typesOfStackItems.length; + } + + public StackMapType[] getTypesOfLocals() { + return typesOfLocals; + } + + public StackMapType[] getTypesOfStackItems() { + return typesOfStackItems; + } + + private boolean invalidFrameType(final int f) { + // @formatter:off + return f != Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED + && !(f >= Const.CHOP_FRAME && f <= Const.CHOP_FRAME_MAX) + && f != Const.SAME_FRAME_EXTENDED + && !(f >= Const.APPEND_FRAME && f <= Const.APPEND_FRAME_MAX) + && f != Const.FULL_FRAME; + // @formatter:on + } + + public void setByteCodeOffset(final int newOffset) { + if (newOffset < 0 || newOffset > 32767) { + throw new IllegalArgumentException("Invalid StackMap offset: " + newOffset); + } + + if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) { + if (newOffset > Const.SAME_FRAME_MAX) { + frameType = Const.SAME_FRAME_EXTENDED; + } else { + frameType = newOffset; + } + } else if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { + if (newOffset > Const.SAME_FRAME_MAX) { + frameType = Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; + } else { + frameType = Const.SAME_LOCALS_1_STACK_ITEM_FRAME + newOffset; + } + } else if (invalidFrameType(frameType)) { + throw new IllegalStateException("Invalid StackMap frameType: " + frameType); + } + byteCodeOffset = newOffset; + } + + /** + * @param constantPool Constant pool to be used for this object. + */ + public void setConstantPool(final ConstantPool constantPool) { + this.constantPool = constantPool; + } + + public void setFrameType(final int ft) { + if (ft >= Const.SAME_FRAME && ft <= Const.SAME_FRAME_MAX) { + byteCodeOffset = ft - Const.SAME_FRAME; + } else if (ft >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && ft <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { + byteCodeOffset = ft - Const.SAME_LOCALS_1_STACK_ITEM_FRAME; + } else if (invalidFrameType(ft)) { + throw new IllegalArgumentException("Invalid StackMap frameType"); + } + frameType = ft; + } + + /** + * + * @deprecated since 6.0 + */ + @java.lang.Deprecated + public void setNumberOfLocals(final int n) { // TODO unused + } + + /** + * + * @deprecated since 6.0 + */ + @java.lang.Deprecated + public void setNumberOfStackItems(final int n) { // TODO unused + } + + public void setTypesOfLocals(final StackMapType[] types) { + typesOfLocals = types != null ? types : StackMapType.EMPTY_ARRAY; + } + + public void setTypesOfStackItems(final StackMapType[] types) { + typesOfStackItems = types != null ? types : StackMapType.EMPTY_ARRAY; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(64); + buf.append("("); + if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) { + buf.append("SAME"); + } else if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { + buf.append("SAME_LOCALS_1_STACK"); + } else if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { + buf.append("SAME_LOCALS_1_STACK_EXTENDED"); + } else if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX) { + buf.append("CHOP ").append(String.valueOf(251 - frameType)); + } else if (frameType == Const.SAME_FRAME_EXTENDED) { + buf.append("SAME_EXTENDED"); + } else if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) { + buf.append("APPEND ").append(String.valueOf(frameType - 251)); + } else if (frameType == Const.FULL_FRAME) { + buf.append("FULL"); + } else { + buf.append("UNKNOWN (").append(frameType).append(")"); + } + buf.append(", offset delta=").append(byteCodeOffset); + if (typesOfLocals.length > 0) { + buf.append(", locals={"); + for (int i = 0; i < typesOfLocals.length; i++) { + buf.append(typesOfLocals[i]); + if (i < typesOfLocals.length - 1) { + buf.append(", "); + } + } + buf.append("}"); + } + if (typesOfStackItems.length > 0) { + buf.append(", stack items={"); + for (int i = 0; i < typesOfStackItems.length; i++) { + buf.append(typesOfStackItems[i]); + if (i < typesOfStackItems.length - 1) { + buf.append(", "); + } + } + buf.append("}"); + } + buf.append(")"); + return buf.toString(); + } + + /** + * Update the distance (as an offset delta) from this StackMap entry to the next. Note that this might cause the + * frame type to change. Note also that delta may be negative. + * + * @param delta offset delta + */ + public void updateByteCodeOffset(final int delta) { + setByteCodeOffset(byteCodeOffset + delta); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/StackMapType.java b/src/main/java/haidnor/jvm/bcel/classfile/StackMapType.java new file mode 100644 index 0000000..6d3b61e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/StackMapType.java @@ -0,0 +1,157 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class represents the type of a local variable or item on stack used in the StackMap entries. + * + * @see StackMapEntry + * @see StackMap + * @see Const + */ +public final class StackMapType implements Cloneable { + + public static final StackMapType[] EMPTY_ARRAY = {}; // must be public because BCELifier code generator writes calls to it + + private byte type; + private int index = -1; // Index to CONSTANT_Class or offset + private ConstantPool constantPool; + + /** + * @param type type tag as defined in the Constants interface + * @param index index to constant pool, or byte code offset + */ + public StackMapType(final byte type, final int index, final ConstantPool constantPool) { + this.type = checkType(type); + this.index = index; + this.constantPool = constantPool; + } + + /** + * Construct object from file stream. + * + * @param file Input stream + * @throws IOException if an I/O error occurs. + */ + StackMapType(final DataInput file, final ConstantPool constantPool) throws IOException { + this(file.readByte(), -1, constantPool); + if (hasIndex()) { + this.index = file.readUnsignedShort(); + } + this.constantPool = constantPool; + } + + private byte checkType(final byte type) { + if (type < Const.ITEM_Bogus || type > Const.ITEM_NewObject) { + throw new ClassFormatException("Illegal type for StackMapType: " + type); + } + return type; + } + + /** + * @return deep copy of this object + */ + public StackMapType copy() { + try { + return (StackMapType) clone(); + } catch (final CloneNotSupportedException e) { + // TODO should this throw? + } + return null; + } + + /** + * Dump type entries to file. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + public void dump(final DataOutputStream file) throws IOException { + file.writeByte(type); + if (hasIndex()) { + file.writeShort(getIndex()); + } + } + + /** + * @return Constant pool used by this object. + */ + public ConstantPool getConstantPool() { + return constantPool; + } + + /** + * @return index to constant pool if type == ITEM_Object, or offset in byte code, if type == ITEM_NewObject, and -1 + * otherwise + */ + public int getIndex() { + return index; + } + + public byte getType() { + return type; + } + + /** + * @return true, if type is either ITEM_Object or ITEM_NewObject + */ + public boolean hasIndex() { + return type == Const.ITEM_Object || type == Const.ITEM_NewObject; + } + + private String printIndex() { + if (type == Const.ITEM_Object) { + if (index < 0) { + return ", class="; + } + return ", class=" + constantPool.constantToString(index, Const.CONSTANT_Class); + } + if (type == Const.ITEM_NewObject) { + return ", offset=" + index; + } + return ""; + } + + /** + * @param constantPool Constant pool to be used for this object. + */ + public void setConstantPool(final ConstantPool constantPool) { + this.constantPool = constantPool; + } + + public void setIndex(final int index) { + this.index = index; + } + + public void setType(final byte type) { + this.type = checkType(type); + } + + /** + * @return String representation + */ + @Override + public String toString() { + return "(type=" + Const.getItemName(type) + printIndex() + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Synthetic.java b/src/main/java/haidnor/jvm/bcel/classfile/Synthetic.java new file mode 100644 index 0000000..f2e8864 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Synthetic.java @@ -0,0 +1,140 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.Args; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is derived from Attribute and declares this class as 'synthetic', i.e., it needs special + * handling. The JVM specification states "A class member that does not appear in the source code must be marked using a + * Synthetic attribute." It may appear in the ClassFile attribute table, a field_info table or a method_info table. This + * class is intended to be instantiated from the Attribute.readAttribute() method. + * + * @see Attribute + */ +public final class Synthetic extends Attribute { + + private byte[] bytes; + + /** + * @param nameIndex Index in constant pool to CONSTANT_Utf8, which should represent the string "Synthetic". + * @param length Content length in bytes - should be zero. + * @param bytes Attribute contents + * @param constantPool The constant pool this attribute is associated with. + */ + public Synthetic(final int nameIndex, final int length, final byte[] bytes, final ConstantPool constantPool) { + super(Const.ATTR_SYNTHETIC, nameIndex, Args.require0(length, "Synthetic attribute length"), constantPool); + this.bytes = bytes; + } + + /** + * Construct object from input stream. + * + * @param nameIndex Index in constant pool to CONSTANT_Utf8 + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + Synthetic(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (byte[]) null, constantPool); + if (length > 0) { + bytes = new byte[length]; + input.readFully(bytes); + println("Synthetic attribute with length > 0"); + } + } + + /** + * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a + * physical copy. + * + * @param c Source to copy. + */ + public Synthetic(final Synthetic c) { + this(c.getNameIndex(), c.getLength(), c.getBytes(), c.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitSynthetic(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final Synthetic c = (Synthetic) clone(); + if (bytes != null) { + c.bytes = bytes.clone(); + } + c.setConstantPool(constantPool); + return c; + } + + /** + * Dump source file attribute to file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + if (super.getLength() > 0) { + file.write(bytes, 0, super.getLength()); + } + } + + /** + * @return data bytes. + */ + public byte[] getBytes() { + return bytes; + } + + /** + * @param bytes + */ + public void setBytes(final byte[] bytes) { + this.bytes = bytes; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder("Synthetic"); + if (super.getLength() > 0) { + buf.append(" ").append(Utility.toHexString(bytes)); + } + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Unknown.java b/src/main/java/haidnor/jvm/bcel/classfile/Unknown.java new file mode 100644 index 0000000..84e59dc --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Unknown.java @@ -0,0 +1,159 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * This class represents a reference to an unknown (i.e., application-specific) attribute of a class. It is instantiated + * from the {@link Attribute#readAttribute(DataInput, ConstantPool)} method. Applications that need to read in + * application-specific attributes should create an {@link UnknownAttributeReader} implementation and attach it via + * {@link Attribute#addAttributeReader(String, UnknownAttributeReader)}. + * + * @see Attribute + * @see UnknownAttributeReader + */ +public final class Unknown extends Attribute { + + private byte[] bytes; + + private final String name; + + /** + * Constructs a new instance for a non-standard attribute. + * + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param bytes Attribute contents + * @param constantPool Array of constants + */ + public Unknown(final int nameIndex, final int length, final byte[] bytes, final ConstantPool constantPool) { + super(Const.ATTR_UNKNOWN, nameIndex, length, constantPool); + this.bytes = bytes; + this.name = constantPool.getConstantUtf8(nameIndex).getBytes(); + } + + /** + * Constructs a new instance from an input stream. + * + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + Unknown(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { + this(nameIndex, length, (byte[]) null, constantPool); + if (length > 0) { + bytes = new byte[length]; + input.readFully(bytes); + } + } + + /** + * Constructs a new instance from another instance. Note that both objects use the same references (shallow copy). Use clone() for a physical copy. + * + * @param unknown Source. + */ + public Unknown(final Unknown unknown) { + this(unknown.getNameIndex(), unknown.getLength(), unknown.getBytes(), unknown.getConstantPool()); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. + * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitUnknown(this); + } + + /** + * @return deep copy of this attribute + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final Unknown c = (Unknown) clone(); + if (bytes != null) { + c.bytes = bytes.clone(); + } + c.setConstantPool(constantPool); + return c; + } + + /** + * Dumps unknown bytes to file stream. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + if (super.getLength() > 0) { + file.write(bytes, 0, super.getLength()); + } + } + + /** + * @return data bytes. + */ + public byte[] getBytes() { + return bytes; + } + + /** + * @return name of attribute. + */ + @Override + public String getName() { + return name; + } + + /** + * @param bytes the bytes to set + */ + public void setBytes(final byte[] bytes) { + this.bytes = bytes; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + if (super.getLength() == 0 || bytes == null) { + return "(Unknown attribute " + name + ")"; + } + String hex; + final int limit = 10; + if (super.getLength() > limit) { + final byte[] tmp = Arrays.copyOf(bytes, limit); + hex = Utility.toHexString(tmp) + "... (truncated)"; + } else { + hex = Utility.toHexString(bytes); + } + return "(Unknown attribute " + name + ": " + hex + ")"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/UnknownAttributeReader.java b/src/main/java/haidnor/jvm/bcel/classfile/UnknownAttributeReader.java new file mode 100644 index 0000000..7822992 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/UnknownAttributeReader.java @@ -0,0 +1,46 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +/** + * Unknown (non-standard) attributes may be read via user-defined factory objects that can be registered with the + * Attribute.addAttributeReader method. These factory objects should implement this interface. + * + * @see Attribute + * @since 6.0 + */ +public interface UnknownAttributeReader { + + /** + * When this attribute reader is added via the static method Attribute.addAttributeReader, an attribute name is + * associated with it. As the class file parser parses attributes, it will call various AttributeReaders based on the + * name of the attributes it is constructing. + * + * @param nameIndex An index into the constant pool, indexing a ConstantUtf8 that represents the name of the attribute. + * @param length The length of the data contained in the attribute. This is written into the constant pool and should + * agree with what the factory expects the length to be. + * @param file This is the data input that the factory needs to read its data from. + * @param constantPool This is the constant pool associated with the Attribute that we are constructing. + * + * @return The user-defined AttributeReader should take this data and use it to construct an attribute. In the case of + * errors, a null can be returned which will cause the parsing of the class file to fail. + * + * @see Attribute#addAttributeReader(String, UnknownAttributeReader) + */ + Attribute createAttribute(int nameIndex, int length, java.io.DataInput file, ConstantPool constantPool); +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Utility.java b/src/main/java/haidnor/jvm/bcel/classfile/Utility.java new file mode 100644 index 0000000..07db873 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Utility.java @@ -0,0 +1,1547 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.ByteSequence; +import org.apache.commons.lang3.ArrayUtils; + +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +/** + * Utility functions that do not really belong to any class in particular. + */ +// @since 6.0 methods are no longer final +public abstract class Utility { + + /** + * Decode characters into bytes. Used by decode() + */ + private static class JavaReader extends FilterReader { + + public JavaReader(final Reader in) { + super(in); + } + + @Override + public int read() throws IOException { + final int b = in.read(); + if (b != ESCAPE_CHAR) { + return b; + } + final int i = in.read(); + if (i < 0) { + return -1; + } + if (i >= '0' && i <= '9' || i >= 'a' && i <= 'f') { // Normal escape + final int j = in.read(); + if (j < 0) { + return -1; + } + final char[] tmp = {(char) i, (char) j}; + return Integer.parseInt(new String(tmp), 16); + } + return MAP_CHAR[i]; + } + + @Override + public int read(final char[] cbuf, final int off, final int len) throws IOException { + for (int i = 0; i < len; i++) { + cbuf[off + i] = (char) read(); + } + return len; + } + } + + /** + * Encode bytes into valid java identifier characters. Used by + * encode() + */ + private static class JavaWriter extends FilterWriter { + + public JavaWriter(final Writer out) { + super(out); + } + + @Override + public void write(final char[] cbuf, final int off, final int len) throws IOException { + for (int i = 0; i < len; i++) { + write(cbuf[off + i]); + } + } + + @Override + public void write(final int b) throws IOException { + if (isJavaIdentifierPart((char) b) && b != ESCAPE_CHAR) { + out.write(b); + } else { + out.write(ESCAPE_CHAR); // Escape character + // Special escape + if (b >= 0 && b < FREE_CHARS) { + out.write(CHAR_MAP[b]); + } else { // Normal escape + final char[] tmp = Integer.toHexString(b).toCharArray(); + if (tmp.length == 1) { + out.write('0'); + out.write(tmp[0]); + } else { + out.write(tmp[0]); + out.write(tmp[1]); + } + } + } + } + + @Override + public void write(final String str, final int off, final int len) throws IOException { + write(str.toCharArray(), off, len); + } + } + + /* + * How many chars have been consumed during parsing in typeSignatureToString(). Read by methodSignatureToString(). Set + * by side effect, but only internally. + */ + private static final ThreadLocal CONSUMER_CHARS = ThreadLocal.withInitial(() -> Integer.valueOf(0)); + + /* + * The 'WIDE' instruction is used in the byte code to allow 16-bit wide indices for local variables. This opcode + * precedes an 'ILOAD', e.g.. The opcode immediately following takes an extra byte which is combined with the following + * byte to form a 16-bit value. + */ + private static boolean wide; + + // A-Z, g-z, _, $ + private static final int FREE_CHARS = 48; + + private static final int[] CHAR_MAP = new int[FREE_CHARS]; + + private static final int[] MAP_CHAR = new int[256]; // Reverse map + + private static final char ESCAPE_CHAR = '$'; + + static { + int j = 0; + for (int i = 'A'; i <= 'Z'; i++) { + CHAR_MAP[j] = i; + MAP_CHAR[i] = j; + j++; + } + for (int i = 'g'; i <= 'z'; i++) { + CHAR_MAP[j] = i; + MAP_CHAR[i] = j; + j++; + } + CHAR_MAP[j] = '$'; + MAP_CHAR['$'] = j; + j++; + CHAR_MAP[j] = '_'; + MAP_CHAR['_'] = j; + } + + /** + * Convert bit field of flags into string such as 'static final'. + * + * @param accessFlags Access flags + * @return String representation of flags + */ + public static String accessToString(final int accessFlags) { + return accessToString(accessFlags, false); + } + + /** + * Convert bit field of flags into string such as 'static final'. + * + * Special case: Classes compiled with new compilers and with the 'ACC_SUPER' flag would be said to be "synchronized". + * This is because SUN used the same value for the flags 'ACC_SUPER' and 'ACC_SYNCHRONIZED'. + * + * @param accessFlags Access flags + * @param forClass access flags are for class qualifiers ? + * @return String representation of flags + */ + public static String accessToString(final int accessFlags, final boolean forClass) { + final StringBuilder buf = new StringBuilder(); + int p = 0; + for (int i = 0; p < Const.MAX_ACC_FLAG_I; i++) { // Loop through known flags + p = pow2(i); + if ((accessFlags & p) != 0) { + /* + * Special case: Classes compiled with new compilers and with the 'ACC_SUPER' flag would be said to be "synchronized". + * This is because SUN used the same value for the flags 'ACC_SUPER' and 'ACC_SYNCHRONIZED'. + */ + if (forClass && (p == Const.ACC_SUPER || p == Const.ACC_INTERFACE)) { + continue; + } + buf.append(Const.getAccessName(i)).append(" "); + } + } + return buf.toString().trim(); + } + + /** + * Convert (signed) byte to (unsigned) short value, i.e., all negative values become positive. + */ + private static short byteToShort(final byte b) { + return b < 0 ? (short) (256 + b) : (short) b; + } + + /** + * @param accessFlags the class flags + * + * @return "class" or "interface", depending on the ACC_INTERFACE flag + */ + public static String classOrInterface(final int accessFlags) { + return (accessFlags & Const.ACC_INTERFACE) != 0 ? "interface" : "class"; + } + + /** + * @return 'flag' with bit 'i' set to 0 + */ + public static int clearBit(final int flag, final int i) { + final int bit = pow2(i); + return (flag & bit) == 0 ? flag : flag ^ bit; + } + + public static String codeToString(final byte[] code, final ConstantPool constantPool, final int index, final int length) { + return codeToString(code, constantPool, index, length, true); + } + + /** + * Disassemble a byte array of JVM byte codes starting from code line 'index' and return the disassembled string + * representation. Decode only 'num' opcodes (including their operands), use -1 if you want to decompile everything. + * + * @param code byte code array + * @param constantPool Array of constants + * @param index offset in 'code' array (number of opcodes, not bytes!) + * @param length number of opcodes to decompile, -1 for all + * @param verbose be verbose, e.g. print constant pool index + * @return String representation of byte codes + */ + public static String codeToString(final byte[] code, final ConstantPool constantPool, final int index, final int length, final boolean verbose) { + final StringBuilder buf = new StringBuilder(code.length * 20); // Should be sufficient // CHECKSTYLE IGNORE MagicNumber + try (ByteSequence stream = new ByteSequence(code)) { + for (int i = 0; i < index; i++) { + codeToString(stream, constantPool, verbose); + } + for (int i = 0; stream.available() > 0; i++) { + if (length < 0 || i < length) { + final String indices = fillup(stream.getIndex() + ":", 6, true, ' '); + buf.append(indices).append(codeToString(stream, constantPool, verbose)).append('\n'); + } + } + } catch (final IOException e) { + throw new ClassFormatException("Byte code error: " + buf.toString(), e); + } + return buf.toString(); + } + + public static String codeToString(final ByteSequence bytes, final ConstantPool constantPool) throws IOException { + return codeToString(bytes, constantPool, true); + } + + /** + * Disassemble a stream of byte codes and return the string representation. + * + * @param bytes stream of bytes + * @param constantPool Array of constants + * @param verbose be verbose, e.g. print constant pool index + * @return String representation of byte code + * + * @throws IOException if a failure from reading from the bytes argument occurs + */ + public static String codeToString(final ByteSequence bytes, final ConstantPool constantPool, final boolean verbose) throws IOException { + final short opcode = (short) bytes.readUnsignedByte(); + int defaultOffset = 0; + int low; + int high; + int npairs; + int index; + int vindex; + int constant; + int[] match; + int[] jumpTable; + int noPadBytes = 0; + int offset; + final StringBuilder buf = new StringBuilder(Const.getOpcodeName(opcode)); + /* + * Special case: Skip (0-3) padding bytes, i.e., the following bytes are 4-byte-aligned + */ + if (opcode == Const.TABLESWITCH || opcode == Const.LOOKUPSWITCH) { + final int remainder = bytes.getIndex() % 4; + noPadBytes = remainder == 0 ? 0 : 4 - remainder; + for (int i = 0; i < noPadBytes; i++) { + byte b; + if ((b = bytes.readByte()) != 0) { + System.err.println("Warning: Padding byte != 0 in " + Const.getOpcodeName(opcode) + ":" + b); + } + } + // Both cases have a field default_offset in common + defaultOffset = bytes.readInt(); + } + switch (opcode) { + /* + * Table switch has variable length arguments. + */ + case Const.TABLESWITCH: + low = bytes.readInt(); + high = bytes.readInt(); + offset = bytes.getIndex() - 12 - noPadBytes - 1; + defaultOffset += offset; + buf.append("\tdefault = ").append(defaultOffset).append(", low = ").append(low).append(", high = ").append(high).append("("); + jumpTable = new int[high - low + 1]; + for (int i = 0; i < jumpTable.length; i++) { + jumpTable[i] = offset + bytes.readInt(); + buf.append(jumpTable[i]); + if (i < jumpTable.length - 1) { + buf.append(", "); + } + } + buf.append(")"); + break; + /* + * Lookup switch has variable length arguments. + */ + case Const.LOOKUPSWITCH: { + npairs = bytes.readInt(); + offset = bytes.getIndex() - 8 - noPadBytes - 1; + match = new int[npairs]; + jumpTable = new int[npairs]; + defaultOffset += offset; + buf.append("\tdefault = ").append(defaultOffset).append(", npairs = ").append(npairs).append(" ("); + for (int i = 0; i < npairs; i++) { + match[i] = bytes.readInt(); + jumpTable[i] = offset + bytes.readInt(); + buf.append("(").append(match[i]).append(", ").append(jumpTable[i]).append(")"); + if (i < npairs - 1) { + buf.append(", "); + } + } + buf.append(")"); + } + break; + /* + * Two address bytes + offset from start of byte stream form the jump target + */ + case Const.GOTO: + case Const.IFEQ: + case Const.IFGE: + case Const.IFGT: + case Const.IFLE: + case Const.IFLT: + case Const.JSR: + case Const.IFNE: + case Const.IFNONNULL: + case Const.IFNULL: + case Const.IF_ACMPEQ: + case Const.IF_ACMPNE: + case Const.IF_ICMPEQ: + case Const.IF_ICMPGE: + case Const.IF_ICMPGT: + case Const.IF_ICMPLE: + case Const.IF_ICMPLT: + case Const.IF_ICMPNE: + buf.append("\t\t#").append(bytes.getIndex() - 1 + bytes.readShort()); + break; + /* + * 32-bit wide jumps + */ + case Const.GOTO_W: + case Const.JSR_W: + buf.append("\t\t#").append(bytes.getIndex() - 1 + bytes.readInt()); + break; + /* + * Index byte references local variable (register) + */ + case Const.ALOAD: + case Const.ASTORE: + case Const.DLOAD: + case Const.DSTORE: + case Const.FLOAD: + case Const.FSTORE: + case Const.ILOAD: + case Const.ISTORE: + case Const.LLOAD: + case Const.LSTORE: + case Const.RET: + if (wide) { + vindex = bytes.readUnsignedShort(); + wide = false; // Clear flag + } else { + vindex = bytes.readUnsignedByte(); + } + buf.append("\t\t%").append(vindex); + break; + /* + * Remember wide byte which is used to form a 16-bit address in the following instruction. Relies on that the method is + * called again with the following opcode. + */ + case Const.WIDE: + wide = true; + buf.append("\t(wide)"); + break; + /* + * Array of basic type. + */ + case Const.NEWARRAY: + buf.append("\t\t<").append(Const.getTypeName(bytes.readByte())).append(">"); + break; + /* + * Access object/class fields. + */ + case Const.GETFIELD: + case Const.GETSTATIC: + case Const.PUTFIELD: + case Const.PUTSTATIC: + index = bytes.readUnsignedShort(); + buf.append("\t\t").append(constantPool.constantToString(index, Const.CONSTANT_Fieldref)).append(verbose ? " (" + index + ")" : ""); + break; + /* + * Operands are references to classes in constant pool + */ + case Const.NEW: + case Const.CHECKCAST: + buf.append("\t"); + //$FALL-THROUGH$ + case Const.INSTANCEOF: + index = bytes.readUnsignedShort(); + buf.append("\t<").append(constantPool.constantToString(index, Const.CONSTANT_Class)).append(">").append(verbose ? " (" + index + ")" : ""); + break; + /* + * Operands are references to methods in constant pool + */ + case Const.INVOKESPECIAL: + case Const.INVOKESTATIC: + index = bytes.readUnsignedShort(); + final Constant c = constantPool.getConstant(index); + // With Java8 operand may be either a CONSTANT_Methodref + // or a CONSTANT_InterfaceMethodref. (markro) + buf.append("\t").append(constantPool.constantToString(index, c.getTag())).append(verbose ? " (" + index + ")" : ""); + break; + case Const.INVOKEVIRTUAL: + index = bytes.readUnsignedShort(); + buf.append("\t").append(constantPool.constantToString(index, Const.CONSTANT_Methodref)).append(verbose ? " (" + index + ")" : ""); + break; + case Const.INVOKEINTERFACE: + index = bytes.readUnsignedShort(); + final int nargs = bytes.readUnsignedByte(); // historical, redundant + buf.append("\t").append(constantPool.constantToString(index, Const.CONSTANT_InterfaceMethodref)).append(verbose ? " (" + index + ")\t" : "") + .append(nargs).append("\t").append(bytes.readUnsignedByte()); // Last byte is a reserved space + break; + case Const.INVOKEDYNAMIC: + index = bytes.readUnsignedShort(); + buf.append("\t").append(constantPool.constantToString(index, Const.CONSTANT_InvokeDynamic)).append(verbose ? " (" + index + ")\t" : "") + .append(bytes.readUnsignedByte()) // Thrid byte is a reserved space + .append(bytes.readUnsignedByte()); // Last byte is a reserved space + break; + /* + * Operands are references to items in constant pool + */ + case Const.LDC_W: + case Const.LDC2_W: + index = bytes.readUnsignedShort(); + buf.append("\t\t").append(constantPool.constantToString(index, constantPool.getConstant(index).getTag())) + .append(verbose ? " (" + index + ")" : ""); + break; + case Const.LDC: + index = bytes.readUnsignedByte(); + buf.append("\t\t").append(constantPool.constantToString(index, constantPool.getConstant(index).getTag())) + .append(verbose ? " (" + index + ")" : ""); + break; + /* + * Array of references. + */ + case Const.ANEWARRAY: + index = bytes.readUnsignedShort(); + buf.append("\t\t<").append(compactClassName(constantPool.getConstantString(index, Const.CONSTANT_Class), false)).append(">") + .append(verbose ? " (" + index + ")" : ""); + break; + /* + * Multidimensional array of references. + */ + case Const.MULTIANEWARRAY: { + index = bytes.readUnsignedShort(); + final int dimensions = bytes.readUnsignedByte(); + buf.append("\t<").append(compactClassName(constantPool.getConstantString(index, Const.CONSTANT_Class), false)).append(">\t").append(dimensions) + .append(verbose ? " (" + index + ")" : ""); + } + break; + /* + * Increment local variable. + */ + case Const.IINC: + if (wide) { + vindex = bytes.readUnsignedShort(); + constant = bytes.readShort(); + wide = false; + } else { + vindex = bytes.readUnsignedByte(); + constant = bytes.readByte(); + } + buf.append("\t\t%").append(vindex).append("\t").append(constant); + break; + default: + if (Const.getNoOfOperands(opcode) > 0) { + for (int i = 0; i < Const.getOperandTypeCount(opcode); i++) { + buf.append("\t\t"); + switch (Const.getOperandType(opcode, i)) { + case Const.T_BYTE: + buf.append(bytes.readByte()); + break; + case Const.T_SHORT: + buf.append(bytes.readShort()); + break; + case Const.T_INT: + buf.append(bytes.readInt()); + break; + default: // Never reached + throw new IllegalStateException("Unreachable default case reached!"); + } + } + } + } + return buf.toString(); + } + + /** + * Shorten long class names, java/lang/String becomes String. + * + * @param str The long class name + * @return Compacted class name + */ + public static String compactClassName(final String str) { + return compactClassName(str, true); + } + + /** + * Shorten long class names, java/lang/String becomes java.lang.String, e.g.. If chopit is + * true the prefix java.lang is also removed. + * + * @param str The long class name + * @param chopit flag that determines whether chopping is executed or not + * @return Compacted class name + */ + public static String compactClassName(final String str, final boolean chopit) { + return compactClassName(str, "java.lang.", chopit); + } + + /** + * Shorten long class name str, i.e., chop off the prefix, if the class name starts with this string + * and the flag chopit is true. Slashes / are converted to dots .. + * + * @param str The long class name + * @param prefix The prefix the get rid off + * @param chopit flag that determines whether chopping is executed or not + * @return Compacted class name + */ + public static String compactClassName(String str, final String prefix, final boolean chopit) { + final int len = prefix.length(); + str = pathToPackage(str); // Is '/' on all systems, even DOS + // If string starts with 'prefix' and contains no further dots + if (chopit && str.startsWith(prefix) && str.substring(len).indexOf('.') == -1) { + str = str.substring(len); + } + return str; + } + + /** + * Escape all occurrences of newline chars '\n', quotes \", etc. + */ + public static String convertString(final String label) { + final char[] ch = label.toCharArray(); + final StringBuilder buf = new StringBuilder(); + for (final char element : ch) { + switch (element) { + case '\n': + buf.append("\\n"); + break; + case '\r': + buf.append("\\r"); + break; + case '\"': + buf.append("\\\""); + break; + case '\'': + buf.append("\\'"); + break; + case '\\': + buf.append("\\\\"); + break; + default: + buf.append(element); + break; + } + } + return buf.toString(); + } + + private static int countBrackets(final String brackets) { + final char[] chars = brackets.toCharArray(); + int count = 0; + boolean open = false; + for (final char c : chars) { + switch (c) { + case '[': + if (open) { + throw new IllegalArgumentException("Illegally nested brackets:" + brackets); + } + open = true; + break; + case ']': + if (!open) { + throw new IllegalArgumentException("Illegally nested brackets:" + brackets); + } + open = false; + count++; + break; + default: + // Don't care + break; + } + } + if (open) { + throw new IllegalArgumentException("Illegally nested brackets:" + brackets); + } + return count; + } + + /** + * Decode a string back to a byte array. + * + * @param s the string to convert + * @param uncompress use gzip to uncompress the stream of bytes + * + * @throws IOException if there's a gzip exception + */ + public static byte[] decode(final String s, final boolean uncompress) throws IOException { + byte[] bytes; + try (JavaReader jr = new JavaReader(new CharArrayReader(s.toCharArray())); ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + int ch; + while ((ch = jr.read()) >= 0) { + bos.write(ch); + } + bytes = bos.toByteArray(); + } + if (uncompress) { + final GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes)); + final byte[] tmp = new byte[bytes.length * 3]; // Rough estimate + int count = 0; + int b; + while ((b = gis.read()) >= 0) { + tmp[count++] = (byte) b; + } + bytes = Arrays.copyOf(tmp, count); + } + return bytes; + } + + /** + * Encode byte array it into Java identifier string, i.e., a string that only contains the following characters: (a, ... + * z, A, ... Z, 0, ... 9, _, $). The encoding algorithm itself is not too clever: if the current byte's ASCII value + * already is a valid Java identifier part, leave it as it is. Otherwise it writes the escape character($) followed by: + * + *
    + *
  • the ASCII value as a hexadecimal string, if the value is not in the range 200..247
  • + *
  • a Java identifier char not used in a lowercase hexadecimal string, if the value is in the range 200..247
  • + *
+ * + *

+ * This operation inflates the original byte array by roughly 40-50% + *

+ * + * @param bytes the byte array to convert + * @param compress use gzip to minimize string + * + * @throws IOException if there's a gzip exception + */ + public static String encode(byte[] bytes, final boolean compress) throws IOException { + if (compress) { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream gos = new GZIPOutputStream(baos)) { + gos.write(bytes, 0, bytes.length); + gos.close(); + bytes = baos.toByteArray(); + } + } + final CharArrayWriter caw = new CharArrayWriter(); + try (JavaWriter jw = new JavaWriter(caw)) { + for (final byte b : bytes) { + final int in = b & 0x000000ff; // Normalize to unsigned + jw.write(in); + } + } + return caw.toString(); + } + + /** + * Fillup char with up to length characters with char 'fill' and justify it left or right. + * + * @param str string to format + * @param length length of desired string + * @param leftJustify format left or right + * @param fill fill character + * @return formatted string + */ + public static String fillup(final String str, final int length, final boolean leftJustify, final char fill) { + final int len = length - str.length(); + final char[] buf = new char[Math.max(len, 0)]; + Arrays.fill(buf, fill); + if (leftJustify) { + return str + new String(buf); + } + return new String(buf) + str; + } + + /** + * Return a string for an integer justified left or right and filled up with 'fill' characters if necessary. + * + * @param i integer to format + * @param length length of desired string + * @param leftJustify format left or right + * @param fill fill character + * @return formatted int + */ + public static String format(final int i, final int length, final boolean leftJustify, final char fill) { + return fillup(Integer.toString(i), length, leftJustify, fill); + } + + /** + * WARNING: + * + * There is some nomenclature confusion through much of the BCEL code base with respect to the terms Descriptor and + * Signature. For the offical definitions see: + * + * @see Descriptors in The Java + * Virtual Machine Specification + * + * @see Signatures in The Java + * Virtual Machine Specification + * + * In brief, a descriptor is a string representing the type of a field or method. Signatures are similar, but more + * complex. Signatures are used to encode declarations written in the Java programming language that use types + * outside the type system of the Java Virtual Machine. They are used to describe the type of any class, interface, + * constructor, method or field whose declaration uses type variables or parameterized types. + * + * To parse a descriptor, call typeSignatureToString. To parse a signature, call signatureToString. + * + * Note that if the signature string is a single, non-generic item, the call to signatureToString reduces to a call + * to typeSignatureToString. Also note, that if you only wish to parse the first item in a longer signature string, + * you should call typeSignatureToString directly. + */ + + /** + * Parse Java type such as "char", or "java.lang.String[]" and return the signature in byte code format, e.g. "C" or + * "[Ljava/lang/String;" respectively. + * + * @param type Java type + * @return byte code signature + */ + public static String getSignature(String type) { + final StringBuilder buf = new StringBuilder(); + final char[] chars = type.toCharArray(); + boolean charFound = false; + boolean delim = false; + int index = -1; + loop: for (int i = 0; i < chars.length; i++) { + switch (chars[i]) { + case ' ': + case '\t': + case '\n': + case '\r': + case '\f': + if (charFound) { + delim = true; + } + break; + case '[': + if (!charFound) { + throw new IllegalArgumentException("Illegal type: " + type); + } + index = i; + break loop; + default: + charFound = true; + if (!delim) { + buf.append(chars[i]); + } + } + } + int brackets = 0; + if (index > 0) { + brackets = countBrackets(type.substring(index)); + } + type = buf.toString(); + buf.setLength(0); + for (int i = 0; i < brackets; i++) { + buf.append('['); + } + boolean found = false; + for (int i = Const.T_BOOLEAN; i <= Const.T_VOID && !found; i++) { + if (Const.getTypeName(i).equals(type)) { + found = true; + buf.append(Const.getShortTypeName(i)); + } + } + if (!found) { + buf.append('L').append(packageToPath(type)).append(';'); + } + return buf.toString(); + } + + /** + * @param ch the character to test if it's part of an identifier + * + * @return true, if character is one of (a, ... z, A, ... Z, 0, ... 9, _) + */ + public static boolean isJavaIdentifierPart(final char ch) { + return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9' || ch == '_'; + } + + /** + * @return true, if bit 'i' in 'flag' is set + */ + public static boolean isSet(final int flag, final int i) { + return (flag & pow2(i)) != 0; + } + + /** + * Converts argument list portion of method signature to string with all class names compacted. + * + * @param signature Method signature + * @return String Array of argument types + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + public static String[] methodSignatureArgumentTypes(final String signature) throws ClassFormatException { + return methodSignatureArgumentTypes(signature, true); + } + + /** + * Converts argument list portion of method signature to string. + * + * @param signature Method signature + * @param chopit flag that determines whether chopping is executed or not + * @return String Array of argument types + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + public static String[] methodSignatureArgumentTypes(final String signature, final boolean chopit) throws ClassFormatException { + final List vec = new ArrayList<>(); + int index; + try { + // Skip any type arguments to read argument declarations between '(' and ')' + index = signature.indexOf('(') + 1; + if (index <= 0) { + throw new ClassFormatException("Invalid method signature: " + signature); + } + while (signature.charAt(index) != ')') { + vec.add(typeSignatureToString(signature.substring(index), chopit)); + // corrected concurrent private static field acess + index += unwrap(CONSUMER_CHARS); // update position + } + } catch (final StringIndexOutOfBoundsException e) { // Should never occur + throw new ClassFormatException("Invalid method signature: " + signature, e); + } + return vec.toArray(ArrayUtils.EMPTY_STRING_ARRAY); + } + + /** + * Converts return type portion of method signature to string with all class names compacted. + * + * @param signature Method signature + * @return String representation of method return type + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + public static String methodSignatureReturnType(final String signature) throws ClassFormatException { + return methodSignatureReturnType(signature, true); + } + + /** + * Converts return type portion of method signature to string. + * + * @param signature Method signature + * @param chopit flag that determines whether chopping is executed or not + * @return String representation of method return type + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + public static String methodSignatureReturnType(final String signature, final boolean chopit) throws ClassFormatException { + int index; + String type; + try { + // Read return type after ')' + index = signature.lastIndexOf(')') + 1; + if (index <= 0) { + throw new ClassFormatException("Invalid method signature: " + signature); + } + type = typeSignatureToString(signature.substring(index), chopit); + } catch (final StringIndexOutOfBoundsException e) { // Should never occur + throw new ClassFormatException("Invalid method signature: " + signature, e); + } + return type; + } + + /** + * Converts method signature to string with all class names compacted. + * + * @param signature to convert + * @param name of method + * @param access flags of method + * @return Human readable signature + */ + public static String methodSignatureToString(final String signature, final String name, final String access) { + return methodSignatureToString(signature, name, access, true); + } + + /** + * Converts method signature to string. + * + * @param signature to convert + * @param name of method + * @param access flags of method + * @param chopit flag that determines whether chopping is executed or not + * @return Human readable signature + */ + public static String methodSignatureToString(final String signature, final String name, final String access, final boolean chopit) { + return methodSignatureToString(signature, name, access, chopit, null); + } + + /** + * This method converts a method signature string into a Java type declaration like 'void main(String[])' and throws a + * 'ClassFormatException' when the parsed type is invalid. + * + * @param signature Method signature + * @param name Method name + * @param access Method access rights + * @param chopit flag that determines whether chopping is executed or not + * @param vars the LocalVariableTable for the method + * @return Java type declaration + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + */ + public static String methodSignatureToString(final String signature, final String name, final String access, final boolean chopit, + final LocalVariableTable vars) throws ClassFormatException { + final StringBuilder buf = new StringBuilder("("); + String type; + int index; + int varIndex = access.contains("static") ? 0 : 1; + try { + // Skip any type arguments to read argument declarations between '(' and ')' + index = signature.indexOf('(') + 1; + if (index <= 0) { + throw new ClassFormatException("Invalid method signature: " + signature); + } + while (signature.charAt(index) != ')') { + final String paramType = typeSignatureToString(signature.substring(index), chopit); + buf.append(paramType); + if (vars != null) { + final LocalVariable l = vars.getLocalVariable(varIndex, 0); + if (l != null) { + buf.append(" ").append(l.getName()); + } + } else { + buf.append(" arg").append(varIndex); + } + if ("double".equals(paramType) || "long".equals(paramType)) { + varIndex += 2; + } else { + varIndex++; + } + buf.append(", "); + // corrected concurrent private static field acess + index += unwrap(CONSUMER_CHARS); // update position + } + index++; // update position + // Read return type after ')' + type = typeSignatureToString(signature.substring(index), chopit); + } catch (final StringIndexOutOfBoundsException e) { // Should never occur + throw new ClassFormatException("Invalid method signature: " + signature, e); + } + // ignore any throws information in the signature + if (buf.length() > 1) { + buf.setLength(buf.length() - 2); + } + buf.append(")"); + return access + (!access.isEmpty() ? " " : "") + // May be an empty string + type + " " + name + buf.toString(); + } + + /** + * Converts string containing the method return and argument types to a byte code method signature. + * + * @param ret Return type of method + * @param argv Types of method arguments + * @return Byte code representation of method signature + * + * @throws ClassFormatException if the signature is for Void + */ + public static String methodTypeToSignature(final String ret, final String[] argv) throws ClassFormatException { + final StringBuilder buf = new StringBuilder("("); + String str; + if (argv != null) { + for (final String element : argv) { + str = getSignature(element); + if (str.endsWith("V")) { + throw new ClassFormatException("Invalid type: " + element); + } + buf.append(str); + } + } + str = getSignature(ret); + buf.append(")").append(str); + return buf.toString(); + } + + /** + * Converts '.'s to '/'s. + * + * @param name Source + * @return converted value + * @since 6.7.0 + */ + public static String packageToPath(final String name) { + return name.replace('.', '/'); + } + + /** + * Converts a path to a package name. + * + * @param str the source path. + * @return a package name. + * @since 6.6.0 + */ + public static String pathToPackage(final String str) { + return str.replace('/', '.'); + } + + private static int pow2(final int n) { + return 1 << n; + } + + public static String printArray(final Object[] obj) { + return printArray(obj, true); + } + + public static String printArray(final Object[] obj, final boolean braces) { + return printArray(obj, braces, false); + } + + public static String printArray(final Object[] obj, final boolean braces, final boolean quote) { + if (obj == null) { + return null; + } + final StringBuilder buf = new StringBuilder(); + if (braces) { + buf.append('{'); + } + for (int i = 0; i < obj.length; i++) { + if (obj[i] != null) { + buf.append(quote ? "\"" : "").append(obj[i]).append(quote ? "\"" : ""); + } else { + buf.append("null"); + } + if (i < obj.length - 1) { + buf.append(", "); + } + } + if (braces) { + buf.append('}'); + } + return buf.toString(); + } + + public static void printArray(final PrintStream out, final Object[] obj) { + out.println(printArray(obj, true)); + } + + public static void printArray(final PrintWriter out, final Object[] obj) { + out.println(printArray(obj, true)); + } + + /** + * Replace all occurrences of old in str with new. + * + * @param str String to permute + * @param old String to be replaced + * @param new_ Replacement string + * @return new String object + */ + public static String replace(String str, final String old, final String new_) { + int index; + int oldIndex; + try { + if (str.contains(old)) { // 'old' found in str + final StringBuilder buf = new StringBuilder(); + oldIndex = 0; // String start offset + // While we have something to replace + while ((index = str.indexOf(old, oldIndex)) != -1) { + buf.append(str, oldIndex, index); // append prefix + buf.append(new_); // append replacement + oldIndex = index + old.length(); // Skip 'old'.length chars + } + buf.append(str.substring(oldIndex)); // append rest of string + str = buf.toString(); + } + } catch (final StringIndexOutOfBoundsException e) { // Should not occur + System.err.println(e); + } + return str; + } + + /** + * Map opcode names to opcode numbers. E.g., return Constants.ALOAD for "aload" + */ + public static short searchOpcode(String name) { + name = name.toLowerCase(Locale.ENGLISH); + for (short i = 0; i < Const.OPCODE_NAMES_LENGTH; i++) { + if (Const.getOpcodeName(i).equals(name)) { + return i; + } + } + return -1; + } + + /** + * @return 'flag' with bit 'i' set to 1 + */ + public static int setBit(final int flag, final int i) { + return flag | pow2(i); + } + + /** + * Converts a signature to a string with all class names compacted. Class, Method and Type signatures are supported. + * Enum and Interface signatures are not supported. + * + * @param signature signature to convert + * @return String containg human readable signature + */ + public static String signatureToString(final String signature) { + return signatureToString(signature, true); + } + + /** + * Converts a signature to a string. Class, Method and Type signatures are supported. Enum and Interface signatures are + * not supported. + * + * @param signature signature to convert + * @param chopit flag that determines whether chopping is executed or not + * @return String containg human readable signature + */ + public static String signatureToString(final String signature, final boolean chopit) { + String type = ""; + String typeParams = ""; + int index = 0; + if (signature.charAt(0) == '<') { + // we have type paramters + typeParams = typeParamTypesToString(signature, chopit); + index += unwrap(CONSUMER_CHARS); // update position + } + if (signature.charAt(index) == '(') { + // We have a Method signature. + // add types of arguments + type = typeParams + typeSignaturesToString(signature.substring(index), chopit, ')'); + index += unwrap(CONSUMER_CHARS); // update position + // add return type + type = type + typeSignatureToString(signature.substring(index), chopit); + index += unwrap(CONSUMER_CHARS); // update position + // ignore any throws information in the signature + return type; + } + // Could be Class or Type... + type = typeSignatureToString(signature.substring(index), chopit); + index += unwrap(CONSUMER_CHARS); // update position + if (typeParams.isEmpty() && index == signature.length()) { + // We have a Type signature. + return type; + } + // We have a Class signature. + final StringBuilder typeClass = new StringBuilder(typeParams); + typeClass.append(" extends "); + typeClass.append(type); + if (index < signature.length()) { + typeClass.append(" implements "); + typeClass.append(typeSignatureToString(signature.substring(index), chopit)); + index += unwrap(CONSUMER_CHARS); // update position + } + while (index < signature.length()) { + typeClass.append(", "); + typeClass.append(typeSignatureToString(signature.substring(index), chopit)); + index += unwrap(CONSUMER_CHARS); // update position + } + return typeClass.toString(); + } + + /** + * Convert bytes into hexadecimal string + * + * @param bytes an array of bytes to convert to hexadecimal + * + * @return bytes as hexadecimal string, e.g. 00 fa 12 ... + */ + public static String toHexString(final byte[] bytes) { + final StringBuilder buf = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + final short b = byteToShort(bytes[i]); + final String hex = Integer.toHexString(b); + if (b < 0x10) { + buf.append('0'); + } + buf.append(hex); + if (i < bytes.length - 1) { + buf.append(' '); + } + } + return buf.toString(); + } + + /** + * Return type of method signature as a byte value as defined in Constants + * + * @param signature in format described above + * @return type of method signature + * @see Const + * + * @throws ClassFormatException if signature is not a method signature + */ + public static byte typeOfMethodSignature(final String signature) throws ClassFormatException { + int index; + try { + if (signature.charAt(0) != '(') { + throw new ClassFormatException("Invalid method signature: " + signature); + } + index = signature.lastIndexOf(')') + 1; + return typeOfSignature(signature.substring(index)); + } catch (final StringIndexOutOfBoundsException e) { + throw new ClassFormatException("Invalid method signature: " + signature, e); + } + } + + /** + * Return type of signature as a byte value as defined in Constants + * + * @param signature in format described above + * @return type of signature + * @see Const + * + * @throws ClassFormatException if signature isn't a known type + */ + public static byte typeOfSignature(final String signature) throws ClassFormatException { + try { + switch (signature.charAt(0)) { + case 'B': + return Const.T_BYTE; + case 'C': + return Const.T_CHAR; + case 'D': + return Const.T_DOUBLE; + case 'F': + return Const.T_FLOAT; + case 'I': + return Const.T_INT; + case 'J': + return Const.T_LONG; + case 'L': + case 'T': + return Const.T_REFERENCE; + case '[': + return Const.T_ARRAY; + case 'V': + return Const.T_VOID; + case 'Z': + return Const.T_BOOLEAN; + case 'S': + return Const.T_SHORT; + case '!': + case '+': + case '*': + return typeOfSignature(signature.substring(1)); + default: + throw new ClassFormatException("Invalid method signature: " + signature); + } + } catch (final StringIndexOutOfBoundsException e) { + throw new ClassFormatException("Invalid method signature: " + signature, e); + } + } + + /** + * Converts a type parameter list signature to a string. + * + * @param signature signature to convert + * @param chopit flag that determines whether chopping is executed or not + * @return String containg human readable signature + */ + private static String typeParamTypesToString(final String signature, final boolean chopit) { + // The first character is guranteed to be '<' + final StringBuilder typeParams = new StringBuilder("<"); + int index = 1; // skip the '<' + // get the first TypeParameter + typeParams.append(typeParamTypeToString(signature.substring(index), chopit)); + index += unwrap(CONSUMER_CHARS); // update position + // are there more TypeParameters? + while (signature.charAt(index) != '>') { + typeParams.append(", "); + typeParams.append(typeParamTypeToString(signature.substring(index), chopit)); + index += unwrap(CONSUMER_CHARS); // update position + } + wrap(CONSUMER_CHARS, index + 1); // account for the '>' char + return typeParams.append(">").toString(); + } + + /** + * Converts a type parameter signature to a string. + * + * @param signature signature to convert + * @param chopit flag that determines whether chopping is executed or not + * @return String containg human readable signature + */ + private static String typeParamTypeToString(final String signature, final boolean chopit) { + int index = signature.indexOf(':'); + if (index <= 0) { + throw new ClassFormatException("Invalid type parameter signature: " + signature); + } + // get the TypeParameter identifier + final StringBuilder typeParam = new StringBuilder(signature.substring(0, index)); + index++; // account for the ':' + if (signature.charAt(index) != ':') { + // we have a class bound + typeParam.append(" extends "); + typeParam.append(typeSignatureToString(signature.substring(index), chopit)); + index += unwrap(CONSUMER_CHARS); // update position + } + // look for interface bounds + while (signature.charAt(index) == ':') { + index++; // skip over the ':' + typeParam.append(" & "); + typeParam.append(typeSignatureToString(signature.substring(index), chopit)); + index += unwrap(CONSUMER_CHARS); // update position + } + wrap(CONSUMER_CHARS, index); + return typeParam.toString(); + } + + /** + * Converts a list of type signatures to a string. + * + * @param signature signature to convert + * @param chopit flag that determines whether chopping is executed or not + * @param term character indicating the end of the list + * @return String containg human readable signature + */ + private static String typeSignaturesToString(final String signature, final boolean chopit, final char term) { + // The first character will be an 'open' that matches the 'close' contained in term. + final StringBuilder typeList = new StringBuilder(signature.substring(0, 1)); + int index = 1; // skip the 'open' character + // get the first Type in the list + if (signature.charAt(index) != term) { + typeList.append(typeSignatureToString(signature.substring(index), chopit)); + index += unwrap(CONSUMER_CHARS); // update position + } + // are there more types in the list? + while (signature.charAt(index) != term) { + typeList.append(", "); + typeList.append(typeSignatureToString(signature.substring(index), chopit)); + index += unwrap(CONSUMER_CHARS); // update position + } + wrap(CONSUMER_CHARS, index + 1); // account for the term char + return typeList.append(term).toString(); + } + + /** + * + * This method converts a type signature string into a Java type declaration such as 'String[]' and throws a + * 'ClassFormatException' when the parsed type is invalid. + * + * @param signature type signature + * @param chopit flag that determines whether chopping is executed or not + * @return string containing human readable type signature + * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file + * @since 6.4.0 + */ + public static String typeSignatureToString(final String signature, final boolean chopit) throws ClassFormatException { + // corrected concurrent private static field acess + wrap(CONSUMER_CHARS, 1); // This is the default, read just one char like 'B' + try { + switch (signature.charAt(0)) { + 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 'T': { // TypeVariableSignature + final int index = signature.indexOf(';'); // Look for closing ';' + if (index < 0) { + throw new ClassFormatException("Invalid type variable signature: " + signature); + } + // corrected concurrent private static field acess + wrap(CONSUMER_CHARS, index + 1); // "Tblabla;" 'T' and ';' are removed + return compactClassName(signature.substring(1, index), chopit); + } + case 'L': { // Full class name + // should this be a while loop? can there be more than + // one generic clause? (markro) + int fromIndex = signature.indexOf('<'); // generic type? + if (fromIndex < 0) { + fromIndex = 0; + } else { + fromIndex = signature.indexOf('>', fromIndex); + if (fromIndex < 0) { + throw new ClassFormatException("Invalid signature: " + signature); + } + } + final int index = signature.indexOf(';', fromIndex); // Look for closing ';' + if (index < 0) { + throw new ClassFormatException("Invalid signature: " + signature); + } + + // check to see if there are any TypeArguments + final int bracketIndex = signature.substring(0, index).indexOf('<'); + if (bracketIndex < 0) { + // just a class identifier + wrap(CONSUMER_CHARS, index + 1); // "Lblabla;" 'L' and ';' are removed + return compactClassName(signature.substring(1, index), chopit); + } + // but make sure we are not looking past the end of the current item + fromIndex = signature.indexOf(';'); + if (fromIndex < 0) { + throw new ClassFormatException("Invalid signature: " + signature); + } + if (fromIndex < bracketIndex) { + // just a class identifier + wrap(CONSUMER_CHARS, fromIndex + 1); // "Lblabla;" 'L' and ';' are removed + return compactClassName(signature.substring(1, fromIndex), chopit); + } + + // we have TypeArguments; build up partial result + // as we recurse for each TypeArgument + final StringBuilder type = new StringBuilder(compactClassName(signature.substring(1, bracketIndex), chopit)).append("<"); + int consumedChars = bracketIndex + 1; // Shadows global var + + // check for wildcards + if (signature.charAt(consumedChars) == '+') { + type.append("? extends "); + consumedChars++; + } else if (signature.charAt(consumedChars) == '-') { + type.append("? super "); + consumedChars++; + } + + // get the first TypeArgument + if (signature.charAt(consumedChars) == '*') { + type.append("?"); + consumedChars++; + } else { + type.append(typeSignatureToString(signature.substring(consumedChars), chopit)); + // update our consumed count by the number of characters the for type argument + consumedChars = unwrap(Utility.CONSUMER_CHARS) + consumedChars; + wrap(Utility.CONSUMER_CHARS, consumedChars); + } + + // are there more TypeArguments? + while (signature.charAt(consumedChars) != '>') { + type.append(", "); + // check for wildcards + if (signature.charAt(consumedChars) == '+') { + type.append("? extends "); + consumedChars++; + } else if (signature.charAt(consumedChars) == '-') { + type.append("? super "); + consumedChars++; + } + if (signature.charAt(consumedChars) == '*') { + type.append("?"); + consumedChars++; + } else { + type.append(typeSignatureToString(signature.substring(consumedChars), chopit)); + // update our consumed count by the number of characters the for type argument + consumedChars = unwrap(Utility.CONSUMER_CHARS) + consumedChars; + wrap(Utility.CONSUMER_CHARS, consumedChars); + } + } + + // process the closing ">" + consumedChars++; + type.append(">"); + + if (signature.charAt(consumedChars) == '.') { + // we have a ClassTypeSignatureSuffix + type.append("."); + // convert SimpleClassTypeSignature to fake ClassTypeSignature + // and then recurse to parse it + type.append(typeSignatureToString("L" + signature.substring(consumedChars + 1), chopit)); + // update our consumed count by the number of characters the for type argument + // note that this count includes the "L" we added, but that is ok + // as it accounts for the "." we didn't consume + consumedChars = unwrap(Utility.CONSUMER_CHARS) + consumedChars; + wrap(Utility.CONSUMER_CHARS, consumedChars); + return type.toString(); + } + if (signature.charAt(consumedChars) != ';') { + throw new ClassFormatException("Invalid signature: " + signature); + } + wrap(Utility.CONSUMER_CHARS, consumedChars + 1); // remove final ";" + return type.toString(); + } + case 'S': + return "short"; + case 'Z': + return "boolean"; + case '[': { // Array declaration + int n; + StringBuilder brackets; + String type; + int consumedChars; // Shadows global var + brackets = new StringBuilder(); // Accumulate []'s + // Count opening brackets and look for optional size argument + for (n = 0; signature.charAt(n) == '['; n++) { + brackets.append("[]"); + } + consumedChars = n; // Remember value + // The rest of the string denotes a '' + type = typeSignatureToString(signature.substring(n), chopit); + // corrected concurrent private static field acess + // Utility.consumed_chars += consumed_chars; is replaced by: + final int temp = unwrap(Utility.CONSUMER_CHARS) + consumedChars; + wrap(Utility.CONSUMER_CHARS, temp); + return type + brackets.toString(); + } + case 'V': + return "void"; + default: + throw new ClassFormatException("Invalid signature: '" + signature + "'"); + } + } catch (final StringIndexOutOfBoundsException e) { // Should never occur + throw new ClassFormatException("Invalid signature: " + signature, e); + } + } + + private static int unwrap(final ThreadLocal tl) { + return tl.get().intValue(); + } + + private static void wrap(final ThreadLocal tl, final int value) { + tl.set(Integer.valueOf(value)); + } + +} diff --git a/src/main/java/haidnor/jvm/bcel/classfile/Visitor.java b/src/main/java/haidnor/jvm/bcel/classfile/Visitor.java new file mode 100644 index 0000000..db7f9be --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/classfile/Visitor.java @@ -0,0 +1,231 @@ +/* + * 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 haidnor.jvm.bcel.classfile; + +/** + * Interface to make use of the Visitor pattern programming style. I.e. a class that implements this interface can + * traverse the contents of a Java class just by calling the 'accept' method which all classes have. + */ +public interface Visitor { + /** + * @since 6.0 + */ + void visitAnnotation(Annotations obj); + + /** + * @since 6.0 + */ + void visitAnnotationDefault(AnnotationDefault obj); + + /** + * @since 6.0 + */ + void visitAnnotationEntry(AnnotationEntry obj); + + /** + * @since 6.0 + */ + void visitBootstrapMethods(BootstrapMethods obj); + + void visitCode(Code obj); + + void visitCodeException(CodeException obj); + + void visitConstantClass(ConstantClass obj); + + void visitConstantDouble(ConstantDouble obj); + + /** + * @since 6.3 + */ + default void visitConstantDynamic(final ConstantDynamic constantDynamic) { + // empty + } + + void visitConstantFieldref(ConstantFieldref obj); + + void visitConstantFloat(ConstantFloat obj); + + void visitConstantInteger(ConstantInteger obj); + + void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj); + + void visitConstantInvokeDynamic(ConstantInvokeDynamic obj); + + void visitConstantLong(ConstantLong obj); + + /** + * @since 6.0 + */ + void visitConstantMethodHandle(ConstantMethodHandle obj); + + void visitConstantMethodref(ConstantMethodref obj); + + /** + * @since 6.0 + */ + void visitConstantMethodType(ConstantMethodType obj); + + /** + * @since 6.1 + */ + void visitConstantModule(ConstantModule constantModule); + + void visitConstantNameAndType(ConstantNameAndType obj); + + /** + * @since 6.1 + */ + void visitConstantPackage(ConstantPackage constantPackage); + + void visitConstantPool(ConstantPool obj); + + void visitConstantString(ConstantString obj); + + void visitConstantUtf8(ConstantUtf8 obj); + + void visitConstantValue(ConstantValue obj); + + void visitDeprecated(Deprecated obj); + + /** + * @since 6.0 + */ + void visitEnclosingMethod(EnclosingMethod obj); + + void visitExceptionTable(ExceptionTable obj); + + void visitField(JavaField obj); + + void visitInnerClass(InnerClass obj); + + void visitInnerClasses(InnerClasses obj); + + void visitJavaClass(JavaClass obj); + + void visitLineNumber(LineNumber obj); + + void visitLineNumberTable(LineNumberTable obj); + + void visitLocalVariable(LocalVariable obj); + + void visitLocalVariableTable(LocalVariableTable obj); + + /** + * @since 6.0 + */ + void visitLocalVariableTypeTable(LocalVariableTypeTable obj); + + void visitMethod(JavaMethod obj); + + /** + * @since 6.4.0 + */ + default void visitMethodParameter(final MethodParameter obj) { + // empty + } + + /** + * @since 6.0 + */ + void visitMethodParameters(MethodParameters obj); + + /** + * @since 6.4.0 + */ + default void visitModule(final Module constantModule) { + // empty + } + + /** + * @since 6.4.0 + */ + default void visitModuleExports(final ModuleExports constantModule) { + // empty + } + + /** + * @since 6.4.0 + */ + default void visitModuleMainClass(final ModuleMainClass obj) { + // empty + } + + /** + * @since 6.4.0 + */ + default void visitModuleOpens(final ModuleOpens constantModule) { + // empty + } + + /** + * @since 6.4.0 + */ + default void visitModulePackages(final ModulePackages constantModule) { + // empty + } + + /** + * @since 6.4.0 + */ + default void visitModuleProvides(final ModuleProvides constantModule) { + // empty + } + + /** + * @since 6.4.0 + */ + default void visitModuleRequires(final ModuleRequires constantModule) { + // empty + } + + /** + * @since 6.4.0 + */ + default void visitNestHost(final NestHost obj) { + // empty + } + + /** + * @since 6.4.0 + */ + default void visitNestMembers(final NestMembers obj) { + // empty + } + + /** + * @since 6.0 + */ + void visitParameterAnnotation(ParameterAnnotations obj); + + /** + * @since 6.0 + */ + void visitParameterAnnotationEntry(ParameterAnnotationEntry obj); + + void visitSignature(Signature obj); + + void visitSourceFile(SourceFile obj); + + void visitStackMap(StackMap obj); + + void visitStackMapEntry(StackMapEntry obj); + + void visitSynthetic(Synthetic obj); + + void visitUnknown(Unknown obj); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/AALOAD.java b/src/main/java/haidnor/jvm/bcel/generic/AALOAD.java new file mode 100644 index 0000000..8b1668b --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/AALOAD.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * AALOAD - Load reference from array + * + *
+ * Stack: ..., arrayref, index -> value
+ * 
+ */ +public class AALOAD extends ArrayInstruction implements StackProducer { + + /** + * Load reference from array + */ + public AALOAD() { + super(Const.AALOAD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitAALOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/AASTORE.java b/src/main/java/haidnor/jvm/bcel/generic/AASTORE.java new file mode 100644 index 0000000..5803af6 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/AASTORE.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * AASTORE - Store into reference array + * + *
+ * Stack: ..., arrayref, index, value -> ...
+ * 
+ */ +public class AASTORE extends ArrayInstruction implements StackConsumer { + + /** + * Store into reference array + */ + public AASTORE() { + super(Const.AASTORE); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitAASTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ACONST_NULL.java b/src/main/java/haidnor/jvm/bcel/generic/ACONST_NULL.java new file mode 100644 index 0000000..681d31e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ACONST_NULL.java @@ -0,0 +1,58 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * ACONST_NULL - Push null reference + * + *
+ * Stack: ... -> ..., null
+ * 
+ */ +public class ACONST_NULL extends Instruction implements PushInstruction, TypedInstruction { + + /** + * Push null reference + */ + public ACONST_NULL() { + super(Const.ACONST_NULL, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitPushInstruction(this); + v.visitTypedInstruction(this); + v.visitACONST_NULL(this); + } + + /** + * @return Type.NULL + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.NULL; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ALOAD.java b/src/main/java/haidnor/jvm/bcel/generic/ALOAD.java new file mode 100644 index 0000000..102648a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ALOAD.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * ALOAD - Load reference from local variable + * + *
+ * Stack: ... -> ..., objectref
+ * 
+ */ +public class ALOAD extends LoadInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + ALOAD() { + super(Const.ALOAD, Const.ALOAD_0); + } + + /** + * Load reference from local variable + * + * @param n index of local variable + */ + public ALOAD(final int n) { + super(Const.ALOAD, Const.ALOAD_0, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + super.accept(v); + v.visitALOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ANEWARRAY.java b/src/main/java/haidnor/jvm/bcel/generic/ANEWARRAY.java new file mode 100644 index 0000000..860c8cd --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ANEWARRAY.java @@ -0,0 +1,71 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * ANEWARRAY - Create new array of references + * + *
+ * Stack: ..., count -> ..., arrayref
+ * 
+ */ +public class ANEWARRAY extends CPInstruction implements LoadClass, AllocationInstruction, ExceptionThrower, StackConsumer, StackProducer { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + ANEWARRAY() { + } + + public ANEWARRAY(final int index) { + super(Const.ANEWARRAY, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitLoadClass(this); + v.visitAllocationInstruction(this); + v.visitExceptionThrower(this); + v.visitStackProducer(this); + v.visitTypedInstruction(this); + v.visitCPInstruction(this); + v.visitANEWARRAY(this); + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_CLASS_AND_INTERFACE_RESOLUTION, ExceptionConst.NEGATIVE_ARRAY_SIZE_EXCEPTION); + } + + @Override + public ObjectType getLoadClassType(final ConstantPoolGen cpg) { + Type t = getType(cpg); + if (t instanceof ArrayType) { + t = ((ArrayType) t).getBasicType(); + } + return t instanceof ObjectType ? (ObjectType) t : null; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ARETURN.java b/src/main/java/haidnor/jvm/bcel/generic/ARETURN.java new file mode 100644 index 0000000..c4e3482 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ARETURN.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * ARETURN - Return reference from method + * + *
+ * Stack: ..., objectref -> <empty>
+ * 
+ */ +public class ARETURN extends ReturnInstruction { + + /** + * Return reference from method + */ + public ARETURN() { + super(Const.ARETURN); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackConsumer(this); + v.visitReturnInstruction(this); + v.visitARETURN(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ARRAYLENGTH.java b/src/main/java/haidnor/jvm/bcel/generic/ARRAYLENGTH.java new file mode 100644 index 0000000..6852dc9 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ARRAYLENGTH.java @@ -0,0 +1,58 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * ARRAYLENGTH - Get length of array + * + *
+ * Stack: ..., arrayref -> ..., length
+ * 
+ */ +public class ARRAYLENGTH extends Instruction implements ExceptionThrower, StackProducer, StackConsumer /* since 6.0 */ { + + /** + * Get length of array + */ + public ARRAYLENGTH() { + super(Const.ARRAYLENGTH, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitStackProducer(this); + v.visitARRAYLENGTH(this); + } + + /** + * @return exceptions this instruction may cause + */ + @Override + public Class[] getExceptions() { + return new Class[] {ExceptionConst.NULL_POINTER_EXCEPTION}; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ASTORE.java b/src/main/java/haidnor/jvm/bcel/generic/ASTORE.java new file mode 100644 index 0000000..e264b11 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ASTORE.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * ASTORE - Store reference into local variable + * + *
+ * Stack ..., objectref -> ...
+ * 
+ */ +public class ASTORE extends StoreInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + ASTORE() { + super(Const.ASTORE, Const.ASTORE_0); + } + + /** + * Store reference into local variable + * + * @param n index of local variable + */ + public ASTORE(final int n) { + super(Const.ASTORE, Const.ASTORE_0, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + super.accept(v); + v.visitASTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ATHROW.java b/src/main/java/haidnor/jvm/bcel/generic/ATHROW.java new file mode 100644 index 0000000..053123e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ATHROW.java @@ -0,0 +1,58 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * ATHROW - Throw exception + * + *
+ * Stack: ..., objectref -> objectref
+ * 
+ */ +public class ATHROW extends Instruction implements UnconditionalBranch, ExceptionThrower { + + /** + * Throw exception + */ + public ATHROW() { + super(Const.ATHROW, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitUnconditionalBranch(this); + v.visitExceptionThrower(this); + v.visitATHROW(this); + } + + /** + * @return exceptions this instruction may cause + */ + @Override + public Class[] getExceptions() { + return new Class[] {ExceptionConst.THROWABLE}; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/AllocationInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/AllocationInstruction.java new file mode 100644 index 0000000..aaabbbe --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/AllocationInstruction.java @@ -0,0 +1,23 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denote family of instructions that allocates space in the heap. + */ +public interface AllocationInstruction { +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/AnnotationElementValueGen.java b/src/main/java/haidnor/jvm/bcel/generic/AnnotationElementValueGen.java new file mode 100644 index 0000000..ac79ba2 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/AnnotationElementValueGen.java @@ -0,0 +1,72 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.AnnotationElementValue; +import haidnor.jvm.bcel.classfile.ElementValue; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * @since 6.0 + */ +public class AnnotationElementValueGen extends ElementValueGen { + // For annotation element values, this is the annotation + private final AnnotationEntryGen a; + + public AnnotationElementValueGen(final AnnotationElementValue value, final ConstantPoolGen cpool, final boolean copyPoolEntries) { + super(ANNOTATION, cpool); + a = new AnnotationEntryGen(value.getAnnotationEntry(), cpool, copyPoolEntries); + } + + public AnnotationElementValueGen(final AnnotationEntryGen a, final ConstantPoolGen cpool) { + super(ANNOTATION, cpool); + this.a = a; + } + + public AnnotationElementValueGen(final int type, final AnnotationEntryGen annotation, final ConstantPoolGen cpool) { + super(type, cpool); + if (type != ANNOTATION) { + throw new IllegalArgumentException("Only element values of type annotation can be built with this ctor - type specified: " + type); + } + this.a = annotation; + } + + @Override + public void dump(final DataOutputStream dos) throws IOException { + dos.writeByte(super.getElementValueType()); // u1 type of value (ANNOTATION == '@') + a.dump(dos); + } + + public AnnotationEntryGen getAnnotation() { + return a; + } + + /** + * Return immutable variant of this AnnotationElementValueGen + */ + @Override + public ElementValue getElementValue() { + return new AnnotationElementValue(super.getElementValueType(), a.getAnnotation(), getConstantPool().getConstantPool()); + } + + @Override + public String stringifyValue() { + throw new UnsupportedOperationException("Not implemented yet"); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/AnnotationEntryGen.java b/src/main/java/haidnor/jvm/bcel/generic/AnnotationEntryGen.java new file mode 100644 index 0000000..efeafb6 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/AnnotationEntryGen.java @@ -0,0 +1,326 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.*; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +/** + * @since 6.0 + */ +public class AnnotationEntryGen { + + static final AnnotationEntryGen[] EMPTY_ARRAY = {}; + + /** + * Converts a list of AnnotationGen objects into a set of attributes that can be attached to the class file. + * + * @param cp The constant pool gen where we can create the necessary name refs + * @param annotationEntryGens An array of AnnotationGen objects + */ + static Attribute[] getAnnotationAttributes(final ConstantPoolGen cp, final AnnotationEntryGen[] annotationEntryGens) { + if (annotationEntryGens.length == 0) { + return Attribute.EMPTY_ARRAY; + } + + try { + int countVisible = 0; + int countInvisible = 0; + + // put the annotations in the right output stream + for (final AnnotationEntryGen a : annotationEntryGens) { + if (a.isRuntimeVisible()) { + countVisible++; + } else { + countInvisible++; + } + } + + final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream(); + final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream(); + try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes); DataOutputStream riaDos = new DataOutputStream(riaBytes)) { + + rvaDos.writeShort(countVisible); + riaDos.writeShort(countInvisible); + + // put the annotations in the right output stream + for (final AnnotationEntryGen a : annotationEntryGens) { + if (a.isRuntimeVisible()) { + a.dump(rvaDos); + } else { + a.dump(riaDos); + } + } + } + + final byte[] rvaData = rvaBytes.toByteArray(); + final byte[] riaData = riaBytes.toByteArray(); + + int rvaIndex = -1; + int riaIndex = -1; + + if (rvaData.length > 2) { + rvaIndex = cp.addUtf8("RuntimeVisibleAnnotations"); + } + if (riaData.length > 2) { + riaIndex = cp.addUtf8("RuntimeInvisibleAnnotations"); + } + + final List newAttributes = new ArrayList<>(); + if (rvaData.length > 2) { + newAttributes + .add(new RuntimeVisibleAnnotations(rvaIndex, rvaData.length, new DataInputStream(new ByteArrayInputStream(rvaData)), cp.getConstantPool())); + } + if (riaData.length > 2) { + newAttributes.add( + new RuntimeInvisibleAnnotations(riaIndex, riaData.length, new DataInputStream(new ByteArrayInputStream(riaData)), cp.getConstantPool())); + } + + return newAttributes.toArray(Attribute.EMPTY_ARRAY); + } catch (final IOException e) { + System.err.println("IOException whilst processing annotations"); + e.printStackTrace(); + } + return null; + } + + /** + * Annotations against a class are stored in one of four attribute kinds: - RuntimeVisibleParameterAnnotations - + * RuntimeInvisibleParameterAnnotations + */ + static Attribute[] getParameterAnnotationAttributes(final ConstantPoolGen cp, + final List[] /* Array of lists, array size depends on #params */ vec) { + final int[] visCount = new int[vec.length]; + int totalVisCount = 0; + final int[] invisCount = new int[vec.length]; + int totalInvisCount = 0; + try { + for (int i = 0; i < vec.length; i++) { + if (vec[i] != null) { + for (final AnnotationEntryGen element : vec[i]) { + if (element.isRuntimeVisible()) { + visCount[i]++; + totalVisCount++; + } else { + invisCount[i]++; + totalInvisCount++; + } + } + } + } + // Lets do the visible ones + final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream(); + try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes)) { + rvaDos.writeByte(vec.length); // First goes number of parameters + for (int i = 0; i < vec.length; i++) { + rvaDos.writeShort(visCount[i]); + if (visCount[i] > 0) { + for (final AnnotationEntryGen element : vec[i]) { + if (element.isRuntimeVisible()) { + element.dump(rvaDos); + } + } + } + } + } + // Lets do the invisible ones + final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream(); + try (DataOutputStream riaDos = new DataOutputStream(riaBytes)) { + riaDos.writeByte(vec.length); // First goes number of parameters + for (int i = 0; i < vec.length; i++) { + riaDos.writeShort(invisCount[i]); + if (invisCount[i] > 0) { + for (final AnnotationEntryGen element : vec[i]) { + if (!element.isRuntimeVisible()) { + element.dump(riaDos); + } + } + } + } + } + final byte[] rvaData = rvaBytes.toByteArray(); + final byte[] riaData = riaBytes.toByteArray(); + int rvaIndex = -1; + int riaIndex = -1; + if (totalVisCount > 0) { + rvaIndex = cp.addUtf8("RuntimeVisibleParameterAnnotations"); + } + if (totalInvisCount > 0) { + riaIndex = cp.addUtf8("RuntimeInvisibleParameterAnnotations"); + } + final List newAttributes = new ArrayList<>(); + if (totalVisCount > 0) { + newAttributes.add(new RuntimeVisibleParameterAnnotations(rvaIndex, rvaData.length, new DataInputStream(new ByteArrayInputStream(rvaData)), + cp.getConstantPool())); + } + if (totalInvisCount > 0) { + newAttributes.add(new RuntimeInvisibleParameterAnnotations(riaIndex, riaData.length, new DataInputStream(new ByteArrayInputStream(riaData)), + cp.getConstantPool())); + } + return newAttributes.toArray(Attribute.EMPTY_ARRAY); + } catch (final IOException e) { + System.err.println("IOException whilst processing parameter annotations"); + e.printStackTrace(); + } + return null; + } + + public static AnnotationEntryGen read(final DataInput dis, final ConstantPoolGen cpool, final boolean b) throws IOException { + final AnnotationEntryGen a = new AnnotationEntryGen(cpool); + a.typeIndex = dis.readUnsignedShort(); + final int elemValuePairCount = dis.readUnsignedShort(); + for (int i = 0; i < elemValuePairCount; i++) { + final int nidx = dis.readUnsignedShort(); + a.addElementNameValuePair(new ElementValuePairGen(nidx, ElementValueGen.readElementValue(dis, cpool), cpool)); + } + a.isRuntimeVisible(b); + return a; + } + + private int typeIndex; + + private List evs; + + private final ConstantPoolGen cpool; + + private boolean isRuntimeVisible; + + /** + * Here we are taking a fixed annotation of type Annotation and building a modifiable AnnotationGen object. If the pool + * passed in is for a different class file, then copyPoolEntries should have been passed as true as that will force us + * to do a deep copy of the annotation and move the cpool entries across. We need to copy the type and the element name + * value pairs and the visibility. + */ + public AnnotationEntryGen(final AnnotationEntry a, final ConstantPoolGen cpool, final boolean copyPoolEntries) { + this.cpool = cpool; + if (copyPoolEntries) { + typeIndex = cpool.addUtf8(a.getAnnotationType()); + } else { + typeIndex = a.getAnnotationTypeIndex(); + } + isRuntimeVisible = a.isRuntimeVisible(); + evs = copyValues(a.getElementValuePairs(), cpool, copyPoolEntries); + } + + private AnnotationEntryGen(final ConstantPoolGen cpool) { + this.cpool = cpool; + } + + public AnnotationEntryGen(final ObjectType type, final List elements, final boolean vis, final ConstantPoolGen cpool) { + this.cpool = cpool; + this.typeIndex = cpool.addUtf8(type.getSignature()); + evs = elements; + isRuntimeVisible = vis; + } + + public void addElementNameValuePair(final ElementValuePairGen evp) { + if (evs == null) { + evs = new ArrayList<>(); + } + evs.add(evp); + } + + private List copyValues(final ElementValuePair[] in, final ConstantPoolGen cpool, final boolean copyPoolEntries) { + final List out = new ArrayList<>(); + for (final ElementValuePair nvp : in) { + out.add(new ElementValuePairGen(nvp, cpool, copyPoolEntries)); + } + return out; + } + + public void dump(final DataOutputStream dos) throws IOException { + dos.writeShort(typeIndex); // u2 index of type name in cpool + dos.writeShort(evs.size()); // u2 element_value pair count + for (final ElementValuePairGen envp : evs) { + envp.dump(dos); + } + } + + /** + * Retrieve an immutable version of this AnnotationGen + */ + public AnnotationEntry getAnnotation() { + final AnnotationEntry a = new AnnotationEntry(typeIndex, cpool.getConstantPool(), isRuntimeVisible); + for (final ElementValuePairGen element : evs) { + a.addElementNameValuePair(element.getElementNameValuePair()); + } + return a; + } + + public int getTypeIndex() { + return typeIndex; + } + + public final String getTypeName() { + return getTypeSignature();// BCELBUG: Should I use this instead? + // Utility.signatureToString(getTypeSignature()); + } + + public final String getTypeSignature() { + // ConstantClass c = (ConstantClass)cpool.getConstant(typeIndex); + final ConstantUtf8 utf8 = (ConstantUtf8) cpool.getConstant(typeIndex/* c.getNameIndex() */); + return utf8.getBytes(); + } + + /** + * Returns list of ElementNameValuePair objects. + * + * @return list of ElementNameValuePair objects. + */ + public List getValues() { + return evs; + } + + public boolean isRuntimeVisible() { + return isRuntimeVisible; + } + + private void isRuntimeVisible(final boolean b) { + isRuntimeVisible = b; + } + + public String toShortString() { + final StringBuilder s = new StringBuilder(); + s.append("@").append(getTypeName()).append("("); + for (int i = 0; i < evs.size(); i++) { + s.append(evs.get(i)); + if (i + 1 < evs.size()) { + s.append(","); + } + } + s.append(")"); + return s.toString(); + } + + @Override + public String toString() { + final StringBuilder s = new StringBuilder(32); // CHECKSTYLE IGNORE MagicNumber + s.append("AnnotationGen:[").append(getTypeName()).append(" #").append(evs.size()).append(" {"); + for (int i = 0; i < evs.size(); i++) { + s.append(evs.get(i)); + if (i + 1 < evs.size()) { + s.append(","); + } + } + s.append("}]"); + return s.toString(); + } + +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ArithmeticInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/ArithmeticInstruction.java new file mode 100644 index 0000000..4743eb4 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ArithmeticInstruction.java @@ -0,0 +1,90 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * Super class for the family of arithmetic instructions. + */ +public abstract class ArithmeticInstruction extends Instruction implements TypedInstruction, StackProducer, StackConsumer { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + ArithmeticInstruction() { + } + + /** + * @param opcode of instruction + */ + protected ArithmeticInstruction(final short opcode) { + super(opcode, (short) 1); + } + + /** + * @return type associated with the instruction + */ + @Override + public Type getType(final ConstantPoolGen cp) { + final short opcode = super.getOpcode(); + switch (opcode) { + case Const.DADD: + case Const.DDIV: + case Const.DMUL: + case Const.DNEG: + case Const.DREM: + case Const.DSUB: + return Type.DOUBLE; + case Const.FADD: + case Const.FDIV: + case Const.FMUL: + case Const.FNEG: + case Const.FREM: + case Const.FSUB: + return Type.FLOAT; + case Const.IADD: + case Const.IAND: + case Const.IDIV: + case Const.IMUL: + case Const.INEG: + case Const.IOR: + case Const.IREM: + case Const.ISHL: + case Const.ISHR: + case Const.ISUB: + case Const.IUSHR: + case Const.IXOR: + return Type.INT; + case Const.LADD: + case Const.LAND: + case Const.LDIV: + case Const.LMUL: + case Const.LNEG: + case Const.LOR: + case Const.LREM: + case Const.LSHL: + case Const.LSHR: + case Const.LSUB: + case Const.LUSHR: + case Const.LXOR: + return Type.LONG; + default: // Never reached + throw new ClassGenException("Unknown type " + opcode); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ArrayElementValueGen.java b/src/main/java/haidnor/jvm/bcel/generic/ArrayElementValueGen.java new file mode 100644 index 0000000..b71d7d2 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ArrayElementValueGen.java @@ -0,0 +1,111 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.ArrayElementValue; +import haidnor.jvm.bcel.classfile.ElementValue; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @since 6.0 + */ +public class ArrayElementValueGen extends ElementValueGen { + // J5TODO: Should we make this an array or a list? A list would be easier to + // modify ... + private final List evalues; + + /** + * @param value + * @param cpool + */ + public ArrayElementValueGen(final ArrayElementValue value, final ConstantPoolGen cpool, final boolean copyPoolEntries) { + super(ARRAY, cpool); + evalues = new ArrayList<>(); + final ElementValue[] in = value.getElementValuesArray(); + for (final ElementValue element : in) { + evalues.add(ElementValueGen.copy(element, cpool, copyPoolEntries)); + } + } + + public ArrayElementValueGen(final ConstantPoolGen cp) { + super(ARRAY, cp); + evalues = new ArrayList<>(); + } + + public ArrayElementValueGen(final int type, final ElementValue[] datums, final ConstantPoolGen cpool) { + super(type, cpool); + if (type != ARRAY) { + throw new IllegalArgumentException("Only element values of type array can be built with this ctor - type specified: " + type); + } + this.evalues = new ArrayList<>(); + for (final ElementValue datum : datums) { + evalues.add(ElementValueGen.copy(datum, cpool, true)); + } + } + + public void addElement(final ElementValueGen gen) { + evalues.add(gen); + } + + @Override + public void dump(final DataOutputStream dos) throws IOException { + dos.writeByte(super.getElementValueType()); // u1 type of value (ARRAY == '[') + dos.writeShort(evalues.size()); + for (final ElementValueGen element : evalues) { + element.dump(dos); + } + } + + /** + * Return immutable variant of this ArrayElementValueGen + */ + @Override + public ElementValue getElementValue() { + final ElementValue[] immutableData = new ElementValue[evalues.size()]; + int i = 0; + for (final ElementValueGen element : evalues) { + immutableData[i++] = element.getElementValue(); + } + return new ArrayElementValue(super.getElementValueType(), immutableData, getConstantPool().getConstantPool()); + } + + public List getElementValues() { + return evalues; + } + + public int getElementValuesSize() { + return evalues.size(); + } + + @Override + public String stringifyValue() { + final StringBuilder sb = new StringBuilder(); + sb.append("["); + String comma = ""; + for (final ElementValueGen element : evalues) { + sb.append(comma); + comma = ","; + sb.append(element.stringifyValue()); + } + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ArrayInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/ArrayInstruction.java new file mode 100644 index 0000000..fdb7803 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ArrayInstruction.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * Super class for instructions dealing with array access such as IALOAD. + */ +public abstract class ArrayInstruction extends Instruction implements ExceptionThrower, TypedInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + ArrayInstruction() { + } + + /** + * @param opcode of instruction + */ + protected ArrayInstruction(final short opcode) { + super(opcode, (short) 1); + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_ARRAY_EXCEPTION); + } + + /** + * @return type associated with the instruction + */ + @Override + public Type getType(final ConstantPoolGen cp) { + final short opcode = super.getOpcode(); + switch (opcode) { + case Const.IALOAD: + case Const.IASTORE: + return Type.INT; + case Const.CALOAD: + case Const.CASTORE: + return Type.CHAR; + case Const.BALOAD: + case Const.BASTORE: + return Type.BYTE; + case Const.SALOAD: + case Const.SASTORE: + return Type.SHORT; + case Const.LALOAD: + case Const.LASTORE: + return Type.LONG; + case Const.DALOAD: + case Const.DASTORE: + return Type.DOUBLE; + case Const.FALOAD: + case Const.FASTORE: + return Type.FLOAT; + case Const.AALOAD: + case Const.AASTORE: + return Type.OBJECT; + default: + throw new ClassGenException("Unknown case in switch" + opcode); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ArrayType.java b/src/main/java/haidnor/jvm/bcel/generic/ArrayType.java new file mode 100644 index 0000000..5f8f746 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ArrayType.java @@ -0,0 +1,135 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * Denotes array type, such as int[][] + */ +public final class ArrayType extends ReferenceType { + + private final int dimensions; + private final Type basicType; + + /** + * Convenience constructor for array type, e.g. int[] + * + * @param type array type, e.g. T_INT + * @param dimensions array dimensions + */ + public ArrayType(final byte type, final int dimensions) { + this(BasicType.getType(type), dimensions); + } + + /** + * Convenience constructor for reference array type, e.g. Object[] + * + * @param className complete name of class (java.lang.String, e.g.) + * @param dimensions array dimensions + */ + public ArrayType(final String className, final int dimensions) { + this(ObjectType.getInstance(className), dimensions); + } + + /** + * Constructor for array of given type + * + * @param type type of array (may be an array itself) + * @param dimensions array dimensions + */ + public ArrayType(final Type type, final int dimensions) { + super(Const.T_ARRAY, ""); + if (dimensions < 1 || dimensions > Const.MAX_BYTE) { + throw new ClassGenException("Invalid number of dimensions: " + dimensions); + } + switch (type.getType()) { + case Const.T_ARRAY: + final ArrayType array = (ArrayType) type; + this.dimensions = dimensions + array.dimensions; + basicType = array.basicType; + break; + case Const.T_VOID: + throw new ClassGenException("Invalid type: void[]"); + default: // Basic type or reference + this.dimensions = dimensions; + basicType = type; + break; + } + final StringBuilder buf = new StringBuilder(); + for (int i = 0; i < this.dimensions; i++) { + buf.append('['); + } + buf.append(basicType.getSignature()); + super.setSignature(buf.toString()); + } + + /** + * @return true if both type objects refer to the same array type. + */ + @Override + public boolean equals(final Object type) { + if (type instanceof ArrayType) { + final ArrayType array = (ArrayType) type; + return array.dimensions == dimensions && array.basicType.equals(basicType); + } + return false; + } + + /** + * @return basic type of array, i.e., for int[][][] the basic type is int + */ + public Type getBasicType() { + return basicType; + } + + /** + * Gets the name of referenced class. + * + * @return name of referenced class. + * @since 6.7.0 + */ + @Override + public String getClassName() { + return signature; + } + + /** + * @return number of dimensions of array + */ + public int getDimensions() { + return dimensions; + } + + /** + * @return element type of array, i.e., for int[][][] the element type is int[][] + */ + public Type getElementType() { + if (dimensions == 1) { + return basicType; + } + return new ArrayType(basicType, dimensions - 1); + } + + /** + * @return a hash code value for the object. + */ + @Override + public int hashCode() { + return basicType.hashCode() ^ dimensions; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/BALOAD.java b/src/main/java/haidnor/jvm/bcel/generic/BALOAD.java new file mode 100644 index 0000000..fa48441 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/BALOAD.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * BALOAD - Load byte or boolean from array + * + *
+ * Stack: ..., arrayref, index -> ..., value
+ * 
+ */ +public class BALOAD extends ArrayInstruction implements StackProducer { + + /** + * Load byte or boolean from array + */ + public BALOAD() { + super(Const.BALOAD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitBALOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/BASTORE.java b/src/main/java/haidnor/jvm/bcel/generic/BASTORE.java new file mode 100644 index 0000000..f53e645 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/BASTORE.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * BASTORE - Store into byte or boolean array + * + *
+ * Stack: ..., arrayref, index, value -> ...
+ * 
+ */ +public class BASTORE extends ArrayInstruction implements StackConsumer { + + /** + * Store byte or boolean into array + */ + public BASTORE() { + super(Const.BASTORE); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitBASTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/BIPUSH.java b/src/main/java/haidnor/jvm/bcel/generic/BIPUSH.java new file mode 100644 index 0000000..373defe --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/BIPUSH.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * BIPUSH - Push byte on stack + * + *
+ * Stack: ... -> ..., value
+ * 
+ */ +public class BIPUSH extends Instruction implements ConstantPushInstruction { + + private byte b; + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + BIPUSH() { + } + + /** + * Push byte on stack + */ + public BIPUSH(final byte b) { + super(Const.BIPUSH, (short) 2); + this.b = b; + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitPushInstruction(this); + v.visitStackProducer(this); + v.visitTypedInstruction(this); + v.visitConstantPushInstruction(this); + v.visitBIPUSH(this); + } + + /** + * Dump instruction as byte code to stream out. + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + super.dump(out); + out.writeByte(b); + } + + /** + * @return Type.BYTE + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.BYTE; + } + + @Override + public Number getValue() { + return Integer.valueOf(b); + } + + /** + * Read needed data (e.g. index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + super.setLength(2); + b = bytes.readByte(); + } + + /** + * @return mnemonic for instruction + */ + @Override + public String toString(final boolean verbose) { + return super.toString(verbose) + " " + b; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/BREAKPOINT.java b/src/main/java/haidnor/jvm/bcel/generic/BREAKPOINT.java new file mode 100644 index 0000000..df7cdca --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/BREAKPOINT.java @@ -0,0 +1,40 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * BREAKPOINT, JVM dependent, ignored by default + */ +public class BREAKPOINT extends Instruction { + + public BREAKPOINT() { + super(Const.BREAKPOINT, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitBREAKPOINT(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/BasicType.java b/src/main/java/haidnor/jvm/bcel/generic/BasicType.java new file mode 100644 index 0000000..5af9a28 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/BasicType.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * Denotes basic type such as int. + */ +public final class BasicType extends Type { + + // @since 6.0 no longer final + public static BasicType getType(final byte type) { + switch (type) { + case Const.T_VOID: + return VOID; + case Const.T_BOOLEAN: + return BOOLEAN; + case Const.T_BYTE: + return BYTE; + case Const.T_SHORT: + return SHORT; + case Const.T_CHAR: + return CHAR; + case Const.T_INT: + return INT; + case Const.T_LONG: + return LONG; + case Const.T_DOUBLE: + return DOUBLE; + case Const.T_FLOAT: + return FLOAT; + default: + throw new ClassGenException("Invalid type: " + type); + } + } + + /** + * Constructor for basic types such as int, long, 'void' + * + * @param type one of T_INT, T_BOOLEAN, ..., T_VOID + * @see Const + */ + BasicType(final byte type) { + super(type, Const.getShortTypeName(type)); + if (type < Const.T_BOOLEAN || type > Const.T_VOID) { + throw new ClassGenException("Invalid type: " + type); + } + } + + /** + * @return true if both type objects refer to the same type + */ + @Override + public boolean equals(final Object type) { + return type instanceof BasicType && ((BasicType) type).getType() == this.getType(); + } + + /** + * @return a hash code value for the object. + */ + @Override + public int hashCode() { + return super.getType(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/BranchHandle.java b/src/main/java/haidnor/jvm/bcel/generic/BranchHandle.java new file mode 100644 index 0000000..deb8a97 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/BranchHandle.java @@ -0,0 +1,100 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * BranchHandle is returned by specialized InstructionList.append() whenever a BranchInstruction is appended. This is + * useful when the target of this instruction is not known at time of creation and must be set later via setTarget(). + * + * @see InstructionHandle + * @see Instruction + * @see InstructionList + */ +public final class BranchHandle extends InstructionHandle { + + /** + * Factory method. + */ + static BranchHandle getBranchHandle(final BranchInstruction i) { + return new BranchHandle(i); + } + + // This is also a cache in case the InstructionHandle#swapInstruction() method is used + // See BCEL-273 + private BranchInstruction bi; // An alias in fact, but saves lots of casts + + private BranchHandle(final BranchInstruction i) { + super(i); + bi = i; + } + + /* + * Override InstructionHandle methods: delegate to branch instruction. Through this overriding all access to the private + * i_position field should be prevented. + */ + @Override + public int getPosition() { + return bi.getPosition(); + } + + /** + * @return target of instruction. + */ + public InstructionHandle getTarget() { + return bi.getTarget(); + } + + /** + * Set new contents. Old instruction is disposed and may not be used anymore. + */ + @Override // This is only done in order to apply the additional type check; could be merged with super impl. + public void setInstruction(final Instruction i) { // TODO could be package-protected? + super.setInstruction(i); + if (!(i instanceof BranchInstruction)) { + throw new ClassGenException("Assigning " + i + " to branch handle which is not a branch instruction"); + } + bi = (BranchInstruction) i; + } + + @Override + void setPosition(final int pos) { + // Original code: i_position = bi.position = pos; + bi.setPosition(pos); + super.setPosition(pos); + } + + /** + * Pass new target to instruction. + */ + public void setTarget(final InstructionHandle ih) { + bi.setTarget(ih); + } + + @Override + protected int updatePosition(final int offset, final int maxOffset) { + final int x = bi.updatePosition(offset, maxOffset); + super.setPosition(bi.getPosition()); + return x; + } + + /** + * Update target of instruction. + */ + public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) { + bi.updateTarget(oldIh, newIh); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/BranchInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/BranchInstruction.java new file mode 100644 index 0000000..e27d21a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/BranchInstruction.java @@ -0,0 +1,256 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Abstract super class for branching instructions like GOTO, IFEQ, etc.. Branch instructions may have a variable + * length, namely GOTO, JSR, LOOKUPSWITCH and TABLESWITCH. + * + * @see InstructionList + */ +public abstract class BranchInstruction extends Instruction implements InstructionTargeter { + + /** + * Used by BranchInstruction, LocalVariableGen, CodeExceptionGen, LineNumberGen + */ + static void notifyTarget(final InstructionHandle oldIh, final InstructionHandle newIh, final InstructionTargeter t) { + if (oldIh != null) { + oldIh.removeTargeter(t); + } + if (newIh != null) { + newIh.addTargeter(t); + } + } + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected int index; // Branch target relative to this instruction + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected InstructionHandle target; // Target object in instruction list + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected int position; // Byte code offset + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + BranchInstruction() { + } + + /** + * Common super constructor + * + * @param opcode Instruction opcode + * @param target instruction to branch to + */ + protected BranchInstruction(final short opcode, final InstructionHandle target) { + super(opcode, (short) 3); + setTarget(target); + } + + /** + * @return true, if ih is target of this instruction + */ + @Override + public boolean containsTarget(final InstructionHandle ih) { + return target == ih; + } + + /** + * Inform target that it's not targeted anymore. + */ + @Override + void dispose() { + setTarget(null); + index = -1; + position = -1; + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(super.getOpcode()); + index = getTargetOffset(); + if (!isValidShort(index)) { + throw new ClassGenException("Branch target offset too large for short: " + index); + } + out.writeShort(index); // May be negative, i.e., point backwards + } + + /** + * @return target offset in byte code + */ + public final int getIndex() { + return index; + } + + /** + * @return the position + * @since 6.0 + */ + protected int getPosition() { + return position; + } + + /** + * @return target of branch instruction + */ + public InstructionHandle getTarget() { + return target; + } + + /** + * @return the offset to this instruction's target + */ + protected int getTargetOffset() { + return getTargetOffset(target); + } + + /** + * @param target branch target + * @return the offset to 'target' relative to this instruction + */ + protected int getTargetOffset(final InstructionHandle target) { + if (target == null) { + throw new ClassGenException("Target of " + super.toString(true) + " is invalid null handle"); + } + final int t = target.getPosition(); + if (t < 0) { + throw new ClassGenException("Invalid branch target position offset for " + super.toString(true) + ":" + t + ":" + target); + } + return t - position; + } + + /** + * Read needed data (e.g. index) from file. Conversion to a InstructionHandle is done in InstructionList(byte[]). + * + * @param bytes input stream + * @param wide wide prefix? + * @see InstructionList + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + super.setLength(3); + index = bytes.readShort(); + } + + /** + * @param index the index to set + * @since 6.0 + */ + protected void setIndex(final int index) { + this.index = index; + } + + /** + * @param position the position to set + * @since 6.0 + */ + protected void setPosition(final int position) { + this.position = position; + } + + /** + * Set branch target + * + * @param target branch target + */ + public void setTarget(final InstructionHandle target) { + notifyTarget(this.target, target, this); + this.target = target; + } + + /** + * Long output format: + * + * <position in byte code> <name of opcode> "["<opcode number>"]" "("<length of instruction>")" + * "<"<target instruction>">" "@"<branch target offset> + * + * @param verbose long/short format switch + * @return mnemonic for instruction + */ + @Override + public String toString(final boolean verbose) { + final String s = super.toString(verbose); + String t = "null"; + if (target != null) { + if (verbose) { + if (target.getInstruction() == this) { + t = ""; + } else if (target.getInstruction() == null) { + t = ""; + } else { + // I'm more interested in the address of the target then + // the instruction located there. + // t = target.getInstruction().toString(false); // Avoid circles + t = "" + target.getPosition(); + } + } else { + index = target.getPosition(); + // index = getTargetOffset(); crashes if positions haven't been set + // t = "" + (index + position); + t = "" + index; + } + } + return s + " -> " + t; + } + + /** + * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable + * length instructions 'setPositions' performs multiple passes over the instruction list to calculate the correct (byte) + * positions and offsets by calling this function. + * + * @param offset additional offset caused by preceding (variable length) instructions + * @param maxOffset the maximum offset that may be caused by these instructions + * @return additional offset caused by possible change of this instruction's length + */ + protected int updatePosition(final int offset, final int maxOffset) { + position += offset; + return 0; + } + + /** + * @param oldIh old target + * @param newIh new target + */ + @Override + public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) { + if (target != oldIh) { + throw new ClassGenException("Not targeting " + oldIh + ", but " + target); + } + setTarget(newIh); + } + +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/CALOAD.java b/src/main/java/haidnor/jvm/bcel/generic/CALOAD.java new file mode 100644 index 0000000..2555688 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/CALOAD.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * CALOAD - Load char from array + * + *
+ * Stack: ..., arrayref, index -> ..., value
+ * 
+ */ +public class CALOAD extends ArrayInstruction implements StackProducer { + + /** + * Load char from array + */ + public CALOAD() { + super(Const.CALOAD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitCALOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/CASTORE.java b/src/main/java/haidnor/jvm/bcel/generic/CASTORE.java new file mode 100644 index 0000000..ca0525e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/CASTORE.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * CASTORE - Store into char array + * + *
+ * Stack: ..., arrayref, index, value -> ...
+ * 
+ */ +public class CASTORE extends ArrayInstruction implements StackConsumer { + + /** + * Store char into array + */ + public CASTORE() { + super(Const.CASTORE); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitCASTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/CHECKCAST.java b/src/main/java/haidnor/jvm/bcel/generic/CHECKCAST.java new file mode 100644 index 0000000..5d0f47d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/CHECKCAST.java @@ -0,0 +1,79 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * CHECKCAST - Check whether object is of given type + * + *
+ * Stack: ..., objectref -> ..., objectref
+ * 
+ */ +public class CHECKCAST extends CPInstruction implements LoadClass, ExceptionThrower, StackProducer, StackConsumer { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + CHECKCAST() { + } + + /** + * Check whether object is of given type + * + * @param index index to class in constant pool + */ + public CHECKCAST(final int index) { + super(Const.CHECKCAST, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitLoadClass(this); + v.visitExceptionThrower(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitTypedInstruction(this); + v.visitCPInstruction(this); + v.visitCHECKCAST(this); + } + + /** + * @return exceptions this instruction may cause + */ + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_CLASS_AND_INTERFACE_RESOLUTION, ExceptionConst.CLASS_CAST_EXCEPTION); + } + + @Override + public ObjectType getLoadClassType(final ConstantPoolGen cpg) { + Type t = getType(cpg); + if (t instanceof ArrayType) { + t = ((ArrayType) t).getBasicType(); + } + return t instanceof ObjectType ? (ObjectType) t : null; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/CPInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/CPInstruction.java new file mode 100644 index 0000000..44ad35d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/CPInstruction.java @@ -0,0 +1,141 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.Constant; +import haidnor.jvm.bcel.classfile.ConstantClass; +import haidnor.jvm.bcel.classfile.ConstantPool; +import haidnor.jvm.bcel.classfile.Utility; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Abstract super class for instructions that use an index into the constant pool such as LDC, INVOKEVIRTUAL, etc. + * + * @see ConstantPoolGen + * @see LDC + * @see INVOKEVIRTUAL + */ +public abstract class CPInstruction extends Instruction implements TypedInstruction, IndexedInstruction { + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected int index; // index to constant pool + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + CPInstruction() { + } + + /** + * @param index to constant pool + */ + protected CPInstruction(final short opcode, final int index) { + super(opcode, (short) 3); + setIndex(index); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(super.getOpcode()); + out.writeShort(index); + } + + /** + * @return index in constant pool referred by this instruction. + */ + @Override + public final int getIndex() { + return index; + } + + /** + * @return type related with this instruction. + */ + @Override + public Type getType(final ConstantPoolGen cpg) { + final ConstantPool cp = cpg.getConstantPool(); + String name = cp.getConstantString(index, Const.CONSTANT_Class); + if (!name.startsWith("[")) { + name = "L" + name + ";"; + } + return Type.getType(name); + } + + /** + * Read needed data (i.e., index) from file. + * + * @param bytes input stream + * @param wide wide prefix? + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + setIndex(bytes.readUnsignedShort()); + super.setLength(3); + } + + /** + * Set the index to constant pool. + * + * @param index in constant pool. + */ + @Override + public void setIndex(final int index) { // TODO could be package-protected? + if (index < 0) { + throw new ClassGenException("Negative index value: " + index); + } + this.index = index; + } + + /** + * Long output format: + * + * <name of opcode> "["<opcode number>"]" "("<length of instruction>")" "<"< constant pool + * index>">" + * + * @param verbose long/short format switch + * @return mnemonic for instruction + */ + @Override + public String toString(final boolean verbose) { + return super.toString(verbose) + " " + index; + } + + /** + * @return mnemonic for instruction with symbolic references resolved + */ + @Override + public String toString(final ConstantPool cp) { + final Constant c = cp.getConstant(index); + String str = cp.constantToString(c); + if (c instanceof ConstantClass) { + str = Utility.packageToPath(str); + } + return Const.getOpcodeName(super.getOpcode()) + " " + str; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ClassElementValueGen.java b/src/main/java/haidnor/jvm/bcel/generic/ClassElementValueGen.java new file mode 100644 index 0000000..e5c5d75 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ClassElementValueGen.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.ClassElementValue; +import haidnor.jvm.bcel.classfile.ConstantUtf8; +import haidnor.jvm.bcel.classfile.ElementValue; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * @since 6.0 + */ +public class ClassElementValueGen extends ElementValueGen { + // For primitive types and string type, this points to the value entry in + // the cpool + // For 'class' this points to the class entry in the cpool + private final int idx; + + public ClassElementValueGen(final ClassElementValue value, final ConstantPoolGen cpool, final boolean copyPoolEntries) { + super(CLASS, cpool); + if (copyPoolEntries) { + // idx = cpool.addClass(value.getClassString()); + idx = cpool.addUtf8(value.getClassString()); + } else { + idx = value.getIndex(); + } + } + + protected ClassElementValueGen(final int typeIdx, final ConstantPoolGen cpool) { + super(ElementValueGen.CLASS, cpool); + this.idx = typeIdx; + } + + public ClassElementValueGen(final ObjectType t, final ConstantPoolGen cpool) { + super(ElementValueGen.CLASS, cpool); + // this.idx = cpool.addClass(t); + idx = cpool.addUtf8(t.getSignature()); + } + + @Override + public void dump(final DataOutputStream dos) throws IOException { + dos.writeByte(super.getElementValueType()); // u1 kind of value + dos.writeShort(idx); + } + + public String getClassString() { + final ConstantUtf8 cu8 = (ConstantUtf8) getConstantPool().getConstant(idx); + return cu8.getBytes(); + // ConstantClass c = (ConstantClass)getConstantPool().getConstant(idx); + // ConstantUtf8 utf8 = + // (ConstantUtf8)getConstantPool().getConstant(c.getNameIndex()); + // return utf8.getBytes(); + } + + /** + * Return immutable variant of this ClassElementValueGen + */ + @Override + public ElementValue getElementValue() { + return new ClassElementValue(super.getElementValueType(), idx, getConstantPool().getConstantPool()); + } + + public int getIndex() { + return idx; + } + + @Override + public String stringifyValue() { + return getClassString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ClassGen.java b/src/main/java/haidnor/jvm/bcel/generic/ClassGen.java new file mode 100644 index 0000000..1a5e1ad --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ClassGen.java @@ -0,0 +1,528 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.*; +import haidnor.jvm.bcel.util.BCELComparator; +import org.apache.commons.lang3.ArrayUtils; + +import java.util.*; + +/** + * Template class for building up a java class. May be initialized with an existing java class (file). + * + * @see JavaClass + */ +public class ClassGen extends AccessFlags implements Cloneable { + + private static BCELComparator bcelComparator = new BCELComparator() { + + @Override + public boolean equals(final Object o1, final Object o2) { + final ClassGen THIS = (ClassGen) o1; + final ClassGen THAT = (ClassGen) o2; + return Objects.equals(THIS.getClassName(), THAT.getClassName()); + } + + @Override + public int hashCode(final Object o) { + final ClassGen THIS = (ClassGen) o; + return THIS.getClassName().hashCode(); + } + }; + + /** + * @return Comparison strategy object + */ + public static BCELComparator getComparator() { + return bcelComparator; + } + + /** + * @param comparator Comparison strategy object + */ + public static void setComparator(final BCELComparator comparator) { + bcelComparator = comparator; + } + + /* + * Corresponds to the fields found in a JavaClass object. + */ + private String className; + private String superClassName; + private final String fileName; + private int classNameIndex = -1; + private int superclassNameIndex = -1; + private int major = Const.MAJOR_1_1; + private int minor = Const.MINOR_1_1; + private ConstantPoolGen cp; // Template for building up constant pool + // ArrayLists instead of arrays to gather fields, methods, etc. + private final List fieldList = new ArrayList<>(); + private final List methodList = new ArrayList<>(); + + private final List attributeList = new ArrayList<>(); + + private final List interfaceList = new ArrayList<>(); + + private final List annotationList = new ArrayList<>(); + + private List observers; + + /** + * Initialize with existing class. + * + * @param clazz JavaClass object (e.g. read from file) + */ + public ClassGen(final JavaClass clazz) { + super(clazz.getAccessFlags()); + classNameIndex = clazz.getClassNameIndex(); + superclassNameIndex = clazz.getSuperclassNameIndex(); + className = clazz.getClassName(); + superClassName = clazz.getSuperclassName(); + fileName = clazz.getSourceFileName(); + cp = new ConstantPoolGen(clazz.getConstantPool()); + major = clazz.getMajor(); + minor = clazz.getMinor(); + final Attribute[] attributes = clazz.getAttributes(); + // J5TODO: Could make unpacking lazy, done on first reference + final AnnotationEntryGen[] annotations = unpackAnnotations(attributes); + Collections.addAll(interfaceList, clazz.getInterfaceNames()); + for (final Attribute attribute : attributes) { + if (!(attribute instanceof Annotations)) { + addAttribute(attribute); + } + } + Collections.addAll(annotationList, annotations); + Collections.addAll(methodList, clazz.getMethods()); + Collections.addAll(fieldList, clazz.getFields()); + } + + /** + * Convenience constructor to set up some important values initially. + * + * @param className fully qualified class name + * @param superClassName fully qualified superclass name + * @param fileName source file name + * @param accessFlags access qualifiers + * @param interfaces implemented interfaces + */ + public ClassGen(final String className, final String superClassName, final String fileName, final int accessFlags, final String[] interfaces) { + this(className, superClassName, fileName, accessFlags, interfaces, new ConstantPoolGen()); + } + + /** + * Convenience constructor to set up some important values initially. + * + * @param className fully qualified class name + * @param superClassName fully qualified superclass name + * @param fileName source file name + * @param accessFlags access qualifiers + * @param interfaces implemented interfaces + * @param cp constant pool to use + */ + public ClassGen(final String className, final String superClassName, final String fileName, final int accessFlags, final String[] interfaces, + final ConstantPoolGen cp) { + super(accessFlags); + this.className = className; + this.superClassName = superClassName; + this.fileName = fileName; + this.cp = cp; + // Put everything needed by default into the constant pool and the vectors + if (fileName != null) { + addAttribute(new SourceFile(cp.addUtf8("SourceFile"), 2, cp.addUtf8(fileName), cp.getConstantPool())); + } + classNameIndex = cp.addClass(className); + superclassNameIndex = cp.addClass(superClassName); + if (interfaces != null) { + Collections.addAll(interfaceList, interfaces); + } + } + + public void addAnnotationEntry(final AnnotationEntryGen a) { + annotationList.add(a); + } + + /** + * Add an attribute to this class. + * + * @param a attribute to add + */ + public void addAttribute(final Attribute a) { + attributeList.add(a); + } + + /** + * Convenience method. + * + * Add an empty constructor to this class that does nothing but calling super(). + * + * @param accessFlags rights for constructor + */ + public void addEmptyConstructor(final int accessFlags) { + final InstructionList il = new InstructionList(); + il.append(InstructionConst.THIS); // Push 'this' + il.append(new INVOKESPECIAL(cp.addMethodref(superClassName, Const.CONSTRUCTOR_NAME, "()V"))); + il.append(InstructionConst.RETURN); + final MethodGen mg = new MethodGen(accessFlags, Type.VOID, Type.NO_ARGS, null, Const.CONSTRUCTOR_NAME, className, il, cp); + mg.setMaxStack(1); + addMethod(mg.getMethod()); + } + + /** + * Add a field to this class. + * + * @param f field to add + */ + public void addField(final JavaField f) { + fieldList.add(f); + } + + /** + * Add an interface to this class, i.e., this class has to implement it. + * + * @param name interface to implement (fully qualified class name) + */ + public void addInterface(final String name) { + interfaceList.add(name); + } + + /** + * Add a method to this class. + * + * @param m method to add + */ + public void addMethod(final JavaMethod m) { + methodList.add(m); + } + + /** + * Add observer for this object. + */ + public void addObserver(final ClassObserver o) { + if (observers == null) { + observers = new ArrayList<>(); + } + observers.add(o); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (final CloneNotSupportedException e) { + throw new Error("Clone Not Supported"); // never happens + } + } + + public boolean containsField(final JavaField f) { + return fieldList.contains(f); + } + + /** + * @return field object with given name, or null + */ + public JavaField containsField(final String name) { + for (final JavaField f : fieldList) { + if (f.getName().equals(name)) { + return f; + } + } + return null; + } + + /** + * @return method object with given name and signature, or null + */ + public JavaMethod containsMethod(final String name, final String signature) { + for (final JavaMethod m : methodList) { + if (m.getName().equals(name) && m.getSignature().equals(signature)) { + return m; + } + } + return null; + } + + /** + * Return value as defined by given BCELComparator strategy. By default two ClassGen objects are said to be equal when + * their class names are equal. + * + * @see Object#equals(Object) + */ + @Override + public boolean equals(final Object obj) { + return bcelComparator.equals(this, obj); + } + + // J5TODO: Should we make calling unpackAnnotations() lazy and put it in here? + public AnnotationEntryGen[] getAnnotationEntries() { + return annotationList.toArray(AnnotationEntryGen.EMPTY_ARRAY); + } + + public Attribute[] getAttributes() { + return attributeList.toArray(Attribute.EMPTY_ARRAY); + } + + public String getClassName() { + return className; + } + + public int getClassNameIndex() { + return classNameIndex; + } + + public ConstantPoolGen getConstantPool() { + return cp; + } + + public JavaField[] getFields() { + return fieldList.toArray(JavaField.EMPTY_ARRAY); + } + + public String getFileName() { + return fileName; + } + + public String[] getInterfaceNames() { + return interfaceList.toArray(ArrayUtils.EMPTY_STRING_ARRAY); + } + + public int[] getInterfaces() { + final int size = interfaceList.size(); + final int[] interfaces = new int[size]; + Arrays.setAll(interfaces, i -> cp.addClass(interfaceList.get(i))); + return interfaces; + } + + /** + * @return the (finally) built up Java class object. + */ + public JavaClass getJavaClass() { + final int[] interfaces = getInterfaces(); + final JavaField[] fields = getFields(); + final JavaMethod[] methods = getMethods(); + Attribute[] attributes = null; + if (annotationList.isEmpty()) { + attributes = getAttributes(); + } else { + // TODO: Sometime later, trash any attributes called 'RuntimeVisibleAnnotations' or 'RuntimeInvisibleAnnotations' + final Attribute[] annAttributes = AnnotationEntryGen.getAnnotationAttributes(cp, getAnnotationEntries()); + attributes = new Attribute[attributeList.size() + annAttributes.length]; + attributeList.toArray(attributes); + System.arraycopy(annAttributes, 0, attributes, attributeList.size(), annAttributes.length); + } + // Must be last since the above calls may still add something to it + final ConstantPool cp = this.cp.getFinalConstantPool(); + return new JavaClass(classNameIndex, superclassNameIndex, fileName, major, minor, super.getAccessFlags(), cp, interfaces, fields, methods, + attributes); + } + + /** + * @return major version number of class file + */ + public int getMajor() { + return major; + } + + public JavaMethod getMethodAt(final int pos) { + return methodList.get(pos); + } + + public JavaMethod[] getMethods() { + return methodList.toArray(JavaMethod.EMPTY_ARRAY); + } + + /** + * @return minor version number of class file + */ + public int getMinor() { + return minor; + } + + public String getSuperclassName() { + return superClassName; + } + + public int getSuperclassNameIndex() { + return superclassNameIndex; + } + + /** + * Return value as defined by given BCELComparator strategy. By default return the hashcode of the class name. + * + * @see Object#hashCode() + */ + @Override + public int hashCode() { + return bcelComparator.hashCode(this); + } + + /** + * Remove an attribute from this class. + * + * @param a attribute to remove + */ + public void removeAttribute(final Attribute a) { + attributeList.remove(a); + } + + /** + * Remove a field to this class. + * + * @param f field to remove + */ + public void removeField(final JavaField f) { + fieldList.remove(f); + } + + /** + * Remove an interface from this class. + * + * @param name interface to remove (fully qualified name) + */ + public void removeInterface(final String name) { + interfaceList.remove(name); + } + + /** + * Remove a method from this class. + * + * @param m method to remove + */ + public void removeMethod(final JavaMethod m) { + methodList.remove(m); + } + + /** + * Remove observer for this object. + */ + public void removeObserver(final ClassObserver o) { + if (observers != null) { + observers.remove(o); + } + } + + /** + * Replace given field with new one. If the old one does not exist add the new_ field to the class anyway. + */ + public void replaceField(final JavaField old, final JavaField newField) { + if (newField == null) { + throw new ClassGenException("Replacement method must not be null"); + } + final int i = fieldList.indexOf(old); + if (i < 0) { + fieldList.add(newField); + } else { + fieldList.set(i, newField); + } + } + + /** + * Replace given method with new one. If the old one does not exist add the newMethod method to the class anyway. + */ + public void replaceMethod(final JavaMethod old, final JavaMethod newMethod) { + if (newMethod == null) { + throw new ClassGenException("Replacement method must not be null"); + } + final int i = methodList.indexOf(old); + if (i < 0) { + methodList.add(newMethod); + } else { + methodList.set(i, newMethod); + } + } + + public void setClassName(final String name) { + className = Utility.pathToPackage(name); + classNameIndex = cp.addClass(name); + } + + public void setClassNameIndex(final int classNameIndex) { + this.classNameIndex = classNameIndex; + this.className = Utility.pathToPackage(cp.getConstantPool().getConstantString(classNameIndex, Const.CONSTANT_Class)); + } + + public void setConstantPool(final ConstantPoolGen constantPool) { + cp = constantPool; + } + + /** + * Set major version number of class file, default value is 45 (JDK 1.1) + * + * @param major major version number + */ + public void setMajor(final int major) { // TODO could be package-protected - only called by test code + this.major = major; + } + + public void setMethodAt(final JavaMethod method, final int pos) { + methodList.set(pos, method); + } + + public void setMethods(final JavaMethod[] methods) { + methodList.clear(); + Collections.addAll(methodList, methods); + } + + /** + * Set minor version number of class file, default value is 3 (JDK 1.1) + * + * @param minor minor version number + */ + public void setMinor(final int minor) { // TODO could be package-protected - only called by test code + this.minor = minor; + } + + public void setSuperclassName(final String name) { + superClassName = Utility.pathToPackage(name); + superclassNameIndex = cp.addClass(name); + } + + public void setSuperclassNameIndex(final int superclassNameIndex) { + this.superclassNameIndex = superclassNameIndex; + superClassName = Utility.pathToPackage(cp.getConstantPool().getConstantString(superclassNameIndex, Const.CONSTANT_Class)); + } + + /** + * Look for attributes representing annotations and unpack them. + */ + private AnnotationEntryGen[] unpackAnnotations(final Attribute[] attrs) { + final List annotationGenObjs = new ArrayList<>(); + for (final Attribute attr : attrs) { + if (attr instanceof RuntimeVisibleAnnotations) { + final RuntimeVisibleAnnotations rva = (RuntimeVisibleAnnotations) attr; + rva.forEach(a -> annotationGenObjs.add(new AnnotationEntryGen(a, getConstantPool(), false))); + } else if (attr instanceof RuntimeInvisibleAnnotations) { + final RuntimeInvisibleAnnotations ria = (RuntimeInvisibleAnnotations) attr; + ria.forEach(a -> annotationGenObjs.add(new AnnotationEntryGen(a, getConstantPool(), false))); + } + } + return annotationGenObjs.toArray(AnnotationEntryGen.EMPTY_ARRAY); + } + + /** + * Call notify() method on all observers. This method is not called automatically whenever the state has changed, but + * has to be called by the user after they have finished editing the object. + */ + public void update() { + if (observers != null) { + for (final ClassObserver observer : observers) { + observer.notify(this); + } + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ClassGenException.java b/src/main/java/haidnor/jvm/bcel/generic/ClassGenException.java new file mode 100644 index 0000000..1a2ad9c --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ClassGenException.java @@ -0,0 +1,36 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Thrown on internal exceptions. + */ +public class ClassGenException extends RuntimeException { + + private static final long serialVersionUID = 7247369755051242791L; + + public ClassGenException() { + } + + public ClassGenException(final String s) { + super(s); + } + + public ClassGenException(final String s, final Throwable initCause) { + super(s, initCause); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ClassObserver.java b/src/main/java/haidnor/jvm/bcel/generic/ClassObserver.java new file mode 100644 index 0000000..7969916 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ClassObserver.java @@ -0,0 +1,26 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Implement this interface if you're interested in changes to a ClassGen object and register yourself with + * addObserver(). + */ +public interface ClassObserver { + + void notify(ClassGen clazz); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/CodeExceptionGen.java b/src/main/java/haidnor/jvm/bcel/generic/CodeExceptionGen.java new file mode 100644 index 0000000..f9a65f9 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/CodeExceptionGen.java @@ -0,0 +1,176 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.CodeException; + +/** + * This class represents an exception handler, i.e., specifies the region where a handler is active and an instruction + * where the actual handling is done. pool as parameters. Opposed to the JVM specification the end of the handled region + * is set to be inclusive, i.e. all instructions between start and end are protected including the start and end + * instructions (handles) themselves. The end of the region is automatically mapped to be exclusive when calling + * getCodeException(), i.e., there is no difference semantically. + * + * @see MethodGen + * @see CodeException + * @see InstructionHandle + */ +public final class CodeExceptionGen implements InstructionTargeter, Cloneable { + + static final CodeExceptionGen[] EMPTY_ARRAY = {}; + + private InstructionHandle startPc; + private InstructionHandle endPc; + private InstructionHandle handlerPc; + private ObjectType catchType; + + /** + * Add an exception handler, i.e., specify region where a handler is active and an instruction where the actual handling + * is done. + * + * @param startPc Start of handled region (inclusive) + * @param endPc End of handled region (inclusive) + * @param handlerPc Where handling is done + * @param catchType which exception is handled, null for ANY + */ + public CodeExceptionGen(final InstructionHandle startPc, final InstructionHandle endPc, final InstructionHandle handlerPc, final ObjectType catchType) { + setStartPC(startPc); + setEndPC(endPc); + setHandlerPC(handlerPc); + this.catchType = catchType; + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (final CloneNotSupportedException e) { + throw new Error("Clone Not Supported"); // never happens + } + } + + /** + * @return true, if ih is target of this handler + */ + @Override + public boolean containsTarget(final InstructionHandle ih) { + return startPc == ih || endPc == ih || handlerPc == ih; + } + + /** Gets the type of the Exception to catch, 'null' for ANY. */ + public ObjectType getCatchType() { + return catchType; + } + + /** + * Get CodeException object.
+ * + * This relies on that the instruction list has already been dumped to byte code or that the 'setPositions' methods + * has been called for the instruction list. + * + * @param cp constant pool + */ + public CodeException getCodeException(final ConstantPoolGen cp) { + return new CodeException(startPc.getPosition(), endPc.getPosition() + endPc.getInstruction().getLength(), handlerPc.getPosition(), + catchType == null ? 0 : cp.addClass(catchType)); + } + + /** + * @return end of handled region (inclusive) + */ + public InstructionHandle getEndPC() { + return endPc; + } + + /** + * @return start of handler + */ + public InstructionHandle getHandlerPC() { + return handlerPc; + } + + /** + * @return start of handled region (inclusive) + */ + public InstructionHandle getStartPC() { + return startPc; + } + + /** Sets the type of the Exception to catch. Set 'null' for ANY. */ + public void setCatchType(final ObjectType catchType) { + this.catchType = catchType; + } + + /* + * Set end of handler + * + * @param endPc End of handled region (inclusive) + */ + public void setEndPC(final InstructionHandle endPc) { // TODO could be package-protected? + BranchInstruction.notifyTarget(this.endPc, endPc, this); + this.endPc = endPc; + } + + /* + * Set handler code + * + * @param handlerPc Start of handler + */ + public void setHandlerPC(final InstructionHandle handlerPc) { // TODO could be package-protected? + BranchInstruction.notifyTarget(this.handlerPc, handlerPc, this); + this.handlerPc = handlerPc; + } + + /* + * Set start of handler + * + * @param startPc Start of handled region (inclusive) + */ + public void setStartPC(final InstructionHandle startPc) { // TODO could be package-protected? + BranchInstruction.notifyTarget(this.startPc, startPc, this); + this.startPc = startPc; + } + + @Override + public String toString() { + return "CodeExceptionGen(" + startPc + ", " + endPc + ", " + handlerPc + ")"; + } + + /** + * @param oldIh old target, either start or end + * @param newIh new target + */ + @Override + public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) { + boolean targeted = false; + if (startPc == oldIh) { + targeted = true; + setStartPC(newIh); + } + if (endPc == oldIh) { + targeted = true; + setEndPC(newIh); + } + if (handlerPc == oldIh) { + targeted = true; + setHandlerPC(newIh); + } + if (!targeted) { + throw new ClassGenException("Not targeting " + oldIh + ", but {" + startPc + ", " + endPc + ", " + handlerPc + "}"); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/CompoundInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/CompoundInstruction.java new file mode 100644 index 0000000..104d96e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/CompoundInstruction.java @@ -0,0 +1,33 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Wrapper class for 'compound' operations, virtual instructions that don't exist as byte code, but give a useful + * meaning. For example, the (virtual) PUSH instruction takes an arbitrary argument and produces the appropriate code at + * dump time (ICONST, LDC, BIPUSH, ...). Also you can use the SWITCH instruction as a useful template for either + * LOOKUPSWITCH or TABLESWITCH. + * + * The interface provides the possibility for the user to write 'templates' or 'macros' for such reusable code patterns. + * + * @see PUSH + * @see SWITCH + */ +public interface CompoundInstruction { + + InstructionList getInstructionList(); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ConstantPoolGen.java b/src/main/java/haidnor/jvm/bcel/generic/ConstantPoolGen.java new file mode 100644 index 0000000..cf7cdd2 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ConstantPoolGen.java @@ -0,0 +1,756 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.*; + +import java.lang.Deprecated; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * This class is used to build up a constant pool. The user adds constants via 'addXXX' methods, 'addString', + * 'addClass', etc.. These methods return an index into the constant pool. Finally, 'getFinalConstantPool()' returns the + * constant pool built up. Intermediate versions of the constant pool can be obtained with 'getConstantPool()'. A + * constant pool has capacity for Constants.MAX_SHORT entries. Note that the first (0) is used by the JVM and that + * Double and Long constants need two slots. + * + * @see Constant + */ +public class ConstantPoolGen { + + private static final int DEFAULT_BUFFER_SIZE = 256; + + private static final String METHODREF_DELIM = ":"; + + private static final String IMETHODREF_DELIM = "#"; + + private static final String FIELDREF_DELIM = "&"; + + private static final String NAT_DELIM = "%"; // Name and Type + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected int size; + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected Constant[] constants; + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getSize() + */ + @Deprecated + protected int index = 1; // First entry (0) used by JVM + + private final Map stringTable = new HashMap<>(); + + private final Map classTable = new HashMap<>(); + + private final Map utf8Table = new HashMap<>(); + + private final Map natTable = new HashMap<>(); + + private final Map cpTable = new HashMap<>(); + + /** + * Constructs a new empty constant pool. + */ + public ConstantPoolGen() { + size = DEFAULT_BUFFER_SIZE; + constants = new Constant[size]; + } + + /** + * Constructs a new instance with the given array of constants. + * + * @param cs array of given constants, new ones will be appended + */ + public ConstantPoolGen(final Constant[] cs) { + final StringBuilder sb = new StringBuilder(DEFAULT_BUFFER_SIZE); + + size = Math.min(Math.max(DEFAULT_BUFFER_SIZE, cs.length + 64), Const.MAX_CP_ENTRIES + 1); + constants = new Constant[size]; + + System.arraycopy(cs, 0, constants, 0, cs.length); + if (cs.length > 0) { + index = cs.length; + } + + for (int i = 1; i < index; i++) { + final Constant c = constants[i]; + if (c instanceof ConstantString) { + final ConstantString s = (ConstantString) c; + final ConstantUtf8 u8 = (ConstantUtf8) constants[s.getStringIndex()]; + final String key = u8.getBytes(); + if (!stringTable.containsKey(key)) { + stringTable.put(key, Integer.valueOf(i)); + } + } else if (c instanceof ConstantClass) { + final ConstantClass s = (ConstantClass) c; + final ConstantUtf8 u8 = (ConstantUtf8) constants[s.getNameIndex()]; + final String key = u8.getBytes(); + if (!classTable.containsKey(key)) { + classTable.put(key, Integer.valueOf(i)); + } + } else if (c instanceof ConstantNameAndType) { + final ConstantNameAndType n = (ConstantNameAndType) c; + final ConstantUtf8 u8NameIdx = (ConstantUtf8) constants[n.getNameIndex()]; + final ConstantUtf8 u8SigIdx = (ConstantUtf8) constants[n.getSignatureIndex()]; + + sb.append(u8NameIdx.getBytes()); + sb.append(NAT_DELIM); + sb.append(u8SigIdx.getBytes()); + final String key = sb.toString(); + sb.delete(0, sb.length()); + + if (!natTable.containsKey(key)) { + natTable.put(key, Integer.valueOf(i)); + } + } else if (c instanceof ConstantUtf8) { + final ConstantUtf8 u = (ConstantUtf8) c; + final String key = u.getBytes(); + if (!utf8Table.containsKey(key)) { + utf8Table.put(key, Integer.valueOf(i)); + } + } else if (c instanceof ConstantCP) { + final ConstantCP m = (ConstantCP) c; + String className; + ConstantUtf8 u8; + + if (c instanceof ConstantInvokeDynamic) { + className = Integer.toString(((ConstantInvokeDynamic) m).getBootstrapMethodAttrIndex()); + } else if (c instanceof ConstantDynamic) { + className = Integer.toString(((ConstantDynamic) m).getBootstrapMethodAttrIndex()); + } else { + final ConstantClass clazz = (ConstantClass) constants[m.getClassIndex()]; + u8 = (ConstantUtf8) constants[clazz.getNameIndex()]; + className = Utility.pathToPackage(u8.getBytes()); + } + + final ConstantNameAndType n = (ConstantNameAndType) constants[m.getNameAndTypeIndex()]; + u8 = (ConstantUtf8) constants[n.getNameIndex()]; + final String methodName = u8.getBytes(); + u8 = (ConstantUtf8) constants[n.getSignatureIndex()]; + final String signature = u8.getBytes(); + + // Since name cannot begin with digit, we can use METHODREF_DELIM without fear of duplicates + String delim = METHODREF_DELIM; + if (c instanceof ConstantInterfaceMethodref) { + delim = IMETHODREF_DELIM; + } else if (c instanceof ConstantFieldref) { + delim = FIELDREF_DELIM; + } + + sb.append(className); + sb.append(delim); + sb.append(methodName); + sb.append(delim); + sb.append(signature); + final String key = sb.toString(); + sb.delete(0, sb.length()); + + if (!cpTable.containsKey(key)) { + cpTable.put(key, Integer.valueOf(i)); + } + } +// else if (c == null) { // entries may be null +// // nothing to do +// } else if (c instanceof ConstantInteger) { +// // nothing to do +// } else if (c instanceof ConstantLong) { +// // nothing to do +// } else if (c instanceof ConstantFloat) { +// // nothing to do +// } else if (c instanceof ConstantDouble) { +// // nothing to do +// } else if (c instanceof org.apache.bcel.classfile.ConstantMethodType) { +// // TODO should this be handled somehow? +// } else if (c instanceof org.apache.bcel.classfile.ConstantMethodHandle) { +// // TODO should this be handled somehow? +// } else if (c instanceof org.apache.bcel.classfile.ConstantModule) { +// // TODO should this be handled somehow? +// } else if (c instanceof org.apache.bcel.classfile.ConstantPackage) { +// // TODO should this be handled somehow? +// } else { +// // Not helpful, should throw an exception. +// assert false : "Unexpected constant type: " + c.getClass().getName(); +// } + } + } + + /** + * Constructs a new instance with the given constant pool. + * + * @param cp the constant pool. + */ + public ConstantPoolGen(final ConstantPool cp) { + this(cp.getConstantPool()); + } + + /** + * Add a reference to an array class (e.g. String[][]) as needed by MULTIANEWARRAY instruction, e.g. to the + * ConstantPool. + * + * @param type type of array class + * @return index of entry + */ + public int addArrayClass(final ArrayType type) { + return addClass_(type.getSignature()); + } + + /** + * Add a new Class reference to the ConstantPool for a given type. + * + * @param type Class to add + * @return index of entry + */ + public int addClass(final ObjectType type) { + return addClass(type.getClassName()); + } + + /** + * Add a new Class reference to the ConstantPool, if it is not already in there. + * + * @param str Class to add + * @return index of entry + */ + public int addClass(final String str) { + return addClass_(Utility.packageToPath(str)); + } + + private int addClass_(final String clazz) { + final int cpRet; + if ((cpRet = lookupClass(clazz)) != -1) { + return cpRet; // Already in CP + } + adjustSize(); + final ConstantClass c = new ConstantClass(addUtf8(clazz)); + final int ret = index; + constants[index++] = c; + return computeIfAbsent(classTable, clazz, ret); + } + + /** + * Adds a constant from another ConstantPool and returns the new index. + * + * @param constant The constant to add. + * @param cpGen Source pool. + * @return index of entry + */ + public int addConstant(final Constant constant, final ConstantPoolGen cpGen) { + final Constant[] constants = cpGen.getConstantPool().getConstantPool(); + switch (constant.getTag()) { + case Const.CONSTANT_String: { + final ConstantString s = (ConstantString) constant; + final ConstantUtf8 u8 = (ConstantUtf8) constants[s.getStringIndex()]; + return addString(u8.getBytes()); + } + case Const.CONSTANT_Class: { + final ConstantClass s = (ConstantClass) constant; + final ConstantUtf8 u8 = (ConstantUtf8) constants[s.getNameIndex()]; + return addClass(u8.getBytes()); + } + case Const.CONSTANT_NameAndType: { + final ConstantNameAndType n = (ConstantNameAndType) constant; + final ConstantUtf8 u8 = (ConstantUtf8) constants[n.getNameIndex()]; + final ConstantUtf8 u8_2 = (ConstantUtf8) constants[n.getSignatureIndex()]; + return addNameAndType(u8.getBytes(), u8_2.getBytes()); + } + case Const.CONSTANT_Utf8: + return addUtf8(((ConstantUtf8) constant).getBytes()); + case Const.CONSTANT_Double: + return addDouble(((ConstantDouble) constant).getBytes()); + case Const.CONSTANT_Float: + return addFloat(((ConstantFloat) constant).getBytes()); + case Const.CONSTANT_Long: + return addLong(((ConstantLong) constant).getBytes()); + case Const.CONSTANT_Integer: + return addInteger(((ConstantInteger) constant).getBytes()); + case Const.CONSTANT_InterfaceMethodref: + case Const.CONSTANT_Methodref: + case Const.CONSTANT_Fieldref: { + final ConstantCP m = (ConstantCP) constant; + final ConstantClass clazz = (ConstantClass) constants[m.getClassIndex()]; + final ConstantNameAndType n = (ConstantNameAndType) constants[m.getNameAndTypeIndex()]; + ConstantUtf8 u8 = (ConstantUtf8) constants[clazz.getNameIndex()]; + final String className = Utility.pathToPackage(u8.getBytes()); + u8 = (ConstantUtf8) constants[n.getNameIndex()]; + final String name = u8.getBytes(); + u8 = (ConstantUtf8) constants[n.getSignatureIndex()]; + final String signature = u8.getBytes(); + switch (constant.getTag()) { + case Const.CONSTANT_InterfaceMethodref: + return addInterfaceMethodref(className, name, signature); + case Const.CONSTANT_Methodref: + return addMethodref(className, name, signature); + case Const.CONSTANT_Fieldref: + return addFieldref(className, name, signature); + default: // Never reached + throw new IllegalArgumentException("Unknown constant type " + constant); + } + } + default: // Never reached + throw new IllegalArgumentException("Unknown constant type " + constant); + } + } + + /** + * Add a new double constant to the ConstantPool, if it is not already in there. + * + * @param n Double number to add + * @return index of entry + */ + public int addDouble(final double n) { + int ret; + if ((ret = lookupDouble(n)) != -1) { + return ret; // Already in CP + } + adjustSize(); + ret = index; + constants[index] = new ConstantDouble(n); + index += 2; // Wastes one entry according to spec + return ret; + } + + /** + * Add a new Fieldref constant to the ConstantPool, if it is not already in there. + * + * @param className class name string to add + * @param fieldName field name string to add + * @param signature signature string to add + * @return index of entry + */ + public int addFieldref(final String className, final String fieldName, final String signature) { + final int cpRet; + if ((cpRet = lookupFieldref(className, fieldName, signature)) != -1) { + return cpRet; // Already in CP + } + adjustSize(); + final int classIndex = addClass(className); + final int nameAndTypeIndex = addNameAndType(fieldName, signature); + final int ret = index; + constants[index++] = new ConstantFieldref(classIndex, nameAndTypeIndex); + return computeIfAbsent(cpTable, className + FIELDREF_DELIM + fieldName + FIELDREF_DELIM + signature, ret); + } + + /** + * Add a new Float constant to the ConstantPool, if it is not already in there. + * + * @param n Float number to add + * @return index of entry + */ + public int addFloat(final float n) { + int ret; + if ((ret = lookupFloat(n)) != -1) { + return ret; // Already in CP + } + adjustSize(); + ret = index; + constants[index++] = new ConstantFloat(n); + return ret; + } + + /** + * Add a new Integer constant to the ConstantPool, if it is not already in there. + * + * @param n integer number to add + * @return index of entry + */ + public int addInteger(final int n) { + int ret; + if ((ret = lookupInteger(n)) != -1) { + return ret; // Already in CP + } + adjustSize(); + ret = index; + constants[index++] = new ConstantInteger(n); + return ret; + } + + public int addInterfaceMethodref(final MethodGen method) { + return addInterfaceMethodref(method.getClassName(), method.getName(), method.getSignature()); + } + + /** + * Add a new InterfaceMethodref constant to the ConstantPool, if it is not already in there. + * + * @param className class name string to add + * @param methodName method name string to add + * @param signature signature string to add + * @return index of entry + */ + public int addInterfaceMethodref(final String className, final String methodName, final String signature) { + final int cpRet; + if ((cpRet = lookupInterfaceMethodref(className, methodName, signature)) != -1) { + return cpRet; // Already in CP + } + adjustSize(); + final int classIndex = addClass(className); + final int nameAndTypeIndex = addNameAndType(methodName, signature); + final int ret = index; + constants[index++] = new ConstantInterfaceMethodref(classIndex, nameAndTypeIndex); + return computeIfAbsent(cpTable, className + IMETHODREF_DELIM + methodName + IMETHODREF_DELIM + signature, ret); + } + + /** + * Add a new long constant to the ConstantPool, if it is not already in there. + * + * @param n Long number to add + * @return index of entry + */ + public int addLong(final long n) { + int ret; + if ((ret = lookupLong(n)) != -1) { + return ret; // Already in CP + } + adjustSize(); + ret = index; + constants[index] = new ConstantLong(n); + index += 2; // Wastes one entry according to spec + return ret; + } + public int addMethodref(final MethodGen method) { + return addMethodref(method.getClassName(), method.getName(), method.getSignature()); + } + + /** + * Add a new Methodref constant to the ConstantPool, if it is not already in there. + * + * @param className class name string to add + * @param methodName method name string to add + * @param signature method signature string to add + * @return index of entry + */ + public int addMethodref(final String className, final String methodName, final String signature) { + final int cpRet; + if ((cpRet = lookupMethodref(className, methodName, signature)) != -1) { + return cpRet; // Already in CP + } + adjustSize(); + final int nameAndTypeIndex = addNameAndType(methodName, signature); + final int classIndex = addClass(className); + final int ret = index; + constants[index++] = new ConstantMethodref(classIndex, nameAndTypeIndex); + return computeIfAbsent(cpTable, className + METHODREF_DELIM + methodName + METHODREF_DELIM + signature, ret); + } + + /** + * Add a new NameAndType constant to the ConstantPool if it is not already in there. + * + * @param name Name string to add + * @param signature signature string to add + * @return index of entry + */ + public int addNameAndType(final String name, final String signature) { + int ret; + if ((ret = lookupNameAndType(name, signature)) != -1) { + return ret; // Already in CP + } + adjustSize(); + final int nameIndex = addUtf8(name); + final int signatureIndex = addUtf8(signature); + ret = index; + constants[index++] = new ConstantNameAndType(nameIndex, signatureIndex); + return computeIfAbsent(natTable, name + NAT_DELIM + signature, ret); + } + + /** + * Add a new String constant to the ConstantPool, if it is not already in there. + * + * @param str String to add + * @return index of entry + */ + public int addString(final String str) { + int ret; + if ((ret = lookupString(str)) != -1) { + return ret; // Already in CP + } + final int utf8 = addUtf8(str); + adjustSize(); + final ConstantString s = new ConstantString(utf8); + ret = index; + constants[index++] = s; + return computeIfAbsent(stringTable, str, ret); + } + + /** + * Add a new Utf8 constant to the ConstantPool, if it is not already in there. + * + * @param n Utf8 string to add + * @return index of entry + */ + public int addUtf8(final String n) { + int ret; + if ((ret = lookupUtf8(n)) != -1) { + return ret; // Already in CP + } + adjustSize(); + ret = index; + constants[index++] = new ConstantUtf8(n); + return computeIfAbsent(utf8Table, n, ret); + } + + /** + * Resize internal array of constants. + */ + protected void adjustSize() { + // 3 extra spaces are needed as some entries may take 3 slots + if (index + 3 >= Const.MAX_CP_ENTRIES + 1) { + throw new IllegalStateException("The number of constants " + (index + 3) + + " is over the size of the constant pool: " + + Const.MAX_CP_ENTRIES); + } + + if (index + 3 >= size) { + final Constant[] cs = constants; + size *= 2; + // the constant array shall not exceed the size of the constant pool + size = Math.min(size, Const.MAX_CP_ENTRIES + 1); + constants = new Constant[size]; + System.arraycopy(cs, 0, constants, 0, index); + } + } + + private int computeIfAbsent(final Map map, final String key, final int value) { + return map.computeIfAbsent(key, k -> Integer.valueOf(value)); + } + + /** + * @param i index in constant pool + * @return constant pool entry at index i + */ + public Constant getConstant(final int i) { + return constants[i]; + } + + /** + * @return intermediate constant pool + */ + public ConstantPool getConstantPool() { + return new ConstantPool(constants); + } + + /** + * @return constant pool with proper length + */ + public ConstantPool getFinalConstantPool() { + return new ConstantPool(Arrays.copyOf(constants, index)); + } + + private int getIndex(final Map map, final String key) { + return toIndex(map.get(key)); + } + + /** + * @return current size of constant pool + */ + public int getSize() { + return index; + } + + /** + * Look for ConstantClass in ConstantPool named 'str'. + * + * @param str String to search for + * @return index on success, -1 otherwise + */ + public int lookupClass(final String str) { + return getIndex(classTable, Utility.packageToPath(str)); + } + + /** + * Look for ConstantDouble in ConstantPool. + * + * @param n Double number to look for + * @return index on success, -1 otherwise + */ + public int lookupDouble(final double n) { + final long bits = Double.doubleToLongBits(n); + for (int i = 1; i < index; i++) { + if (constants[i] instanceof ConstantDouble) { + final ConstantDouble c = (ConstantDouble) constants[i]; + if (Double.doubleToLongBits(c.getBytes()) == bits) { + return i; + } + } + } + return -1; + } + + /** + * Look for ConstantFieldref in ConstantPool. + * + * @param className Where to find method + * @param fieldName Guess what + * @param signature return and argument types + * @return index on success, -1 otherwise + */ + public int lookupFieldref(final String className, final String fieldName, final String signature) { + return getIndex(cpTable, className + FIELDREF_DELIM + fieldName + FIELDREF_DELIM + signature); + } + + /** + * Look for ConstantFloat in ConstantPool. + * + * @param n Float number to look for + * @return index on success, -1 otherwise + */ + public int lookupFloat(final float n) { + final int bits = Float.floatToIntBits(n); + for (int i = 1; i < index; i++) { + if (constants[i] instanceof ConstantFloat) { + final ConstantFloat c = (ConstantFloat) constants[i]; + if (Float.floatToIntBits(c.getBytes()) == bits) { + return i; + } + } + } + return -1; + } + + /** + * Look for ConstantInteger in ConstantPool. + * + * @param n integer number to look for + * @return index on success, -1 otherwise + */ + public int lookupInteger(final int n) { + for (int i = 1; i < index; i++) { + if (constants[i] instanceof ConstantInteger) { + final ConstantInteger c = (ConstantInteger) constants[i]; + if (c.getBytes() == n) { + return i; + } + } + } + return -1; + } + + public int lookupInterfaceMethodref(final MethodGen method) { + return lookupInterfaceMethodref(method.getClassName(), method.getName(), method.getSignature()); + } + + /** + * Look for ConstantInterfaceMethodref in ConstantPool. + * + * @param className Where to find method + * @param methodName Guess what + * @param signature return and argument types + * @return index on success, -1 otherwise + */ + public int lookupInterfaceMethodref(final String className, final String methodName, final String signature) { + return getIndex(cpTable, className + IMETHODREF_DELIM + methodName + IMETHODREF_DELIM + signature); + } + + /** + * Look for ConstantLong in ConstantPool. + * + * @param n Long number to look for + * @return index on success, -1 otherwise + */ + public int lookupLong(final long n) { + for (int i = 1; i < index; i++) { + if (constants[i] instanceof ConstantLong) { + final ConstantLong c = (ConstantLong) constants[i]; + if (c.getBytes() == n) { + return i; + } + } + } + return -1; + } + + public int lookupMethodref(final MethodGen method) { + return lookupMethodref(method.getClassName(), method.getName(), method.getSignature()); + } + + /** + * Look for ConstantMethodref in ConstantPool. + * + * @param className Where to find method + * @param methodName Guess what + * @param signature return and argument types + * @return index on success, -1 otherwise + */ + public int lookupMethodref(final String className, final String methodName, final String signature) { + return getIndex(cpTable, className + METHODREF_DELIM + methodName + METHODREF_DELIM + signature); + } + + /** + * Look for ConstantNameAndType in ConstantPool. + * + * @param name of variable/method + * @param signature of variable/method + * @return index on success, -1 otherwise + */ + public int lookupNameAndType(final String name, final String signature) { + return getIndex(natTable, name + NAT_DELIM + signature); + } + + /** + * Look for ConstantString in ConstantPool containing String 'str'. + * + * @param str String to search for + * @return index on success, -1 otherwise + */ + public int lookupString(final String str) { + return getIndex(stringTable, str); + } + + /** + * Look for ConstantUtf8 in ConstantPool. + * + * @param n Utf8 string to look for + * @return index on success, -1 otherwise + */ + public int lookupUtf8(final String n) { + return getIndex(utf8Table, n); + } + + /** + * Use with care! + * + * @param i index in constant pool + * @param c new constant pool entry at index i + */ + public void setConstant(final int i, final Constant c) { + constants[i] = c; + } + + private int toIndex(final Integer index) { + return index != null ? index.intValue() : -1; + } + + /** + * @return String representation. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + for (int i = 1; i < index; i++) { + buf.append(i).append(")").append(constants[i]).append("\n"); + } + return buf.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ConstantPushInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/ConstantPushInstruction.java new file mode 100644 index 0000000..9112c60 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ConstantPushInstruction.java @@ -0,0 +1,29 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denotes a push instruction that produces a literal on the stack such as SIPUSH, BIPUSH, ICONST, etc. + * + * + * @see ICONST + * @see SIPUSH + */ +public interface ConstantPushInstruction extends PushInstruction, TypedInstruction { + + Number getValue(); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ConversionInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/ConversionInstruction.java new file mode 100644 index 0000000..abfbd19 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ConversionInstruction.java @@ -0,0 +1,72 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * Super class for the x2y family of instructions. + */ +public abstract class ConversionInstruction extends Instruction implements TypedInstruction, StackProducer, StackConsumer { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + ConversionInstruction() { + } + + /** + * @param opcode opcode of instruction + */ + protected ConversionInstruction(final short opcode) { + super(opcode, (short) 1); + } + + /** + * @return type associated with the instruction + */ + @Override + public Type getType(final ConstantPoolGen cp) { + final short opcode = super.getOpcode(); + switch (opcode) { + case Const.D2I: + case Const.F2I: + case Const.L2I: + return Type.INT; + case Const.D2F: + case Const.I2F: + case Const.L2F: + return Type.FLOAT; + case Const.D2L: + case Const.F2L: + case Const.I2L: + return Type.LONG; + case Const.F2D: + case Const.I2D: + case Const.L2D: + return Type.DOUBLE; + case Const.I2B: + return Type.BYTE; + case Const.I2C: + return Type.CHAR; + case Const.I2S: + return Type.SHORT; + default: // Never reached + throw new ClassGenException("Unknown type " + opcode); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/D2F.java b/src/main/java/haidnor/jvm/bcel/generic/D2F.java new file mode 100644 index 0000000..d18f995 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/D2F.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * D2F - Convert double to float + * + *
+ * Stack: ..., value.word1, value.word2 -> ..., result
+ * 
+ */ +public class D2F extends ConversionInstruction { + + /** + * Convert double to float + */ + public D2F() { + super(Const.D2F); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitD2F(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/D2I.java b/src/main/java/haidnor/jvm/bcel/generic/D2I.java new file mode 100644 index 0000000..eff782b --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/D2I.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * D2I - Convert double to int + * + *
+ * Stack: ..., value.word1, value.word2 -> ..., result
+ * 
+ */ +public class D2I extends ConversionInstruction { + + /** + * Convert double to int + */ + public D2I() { + super(Const.D2I); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitD2I(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/D2L.java b/src/main/java/haidnor/jvm/bcel/generic/D2L.java new file mode 100644 index 0000000..8f5b715 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/D2L.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * D2L - Convert double to long + * + *
+ * Stack: ..., value.word1, value.word2 -> ..., result.word1, result.word2
+ * 
+ */ +public class D2L extends ConversionInstruction { + + /** + * Convert double to long + */ + public D2L() { + super(Const.D2L); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitD2L(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DADD.java b/src/main/java/haidnor/jvm/bcel/generic/DADD.java new file mode 100644 index 0000000..eb51578 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DADD.java @@ -0,0 +1,53 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DADD - Add doubles + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 ->
+ * 
+ * + * ..., result.word1, result1.word2 + */ +public class DADD extends ArithmeticInstruction { + + /** + * Add doubles + */ + public DADD() { + super(Const.DADD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitDADD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DALOAD.java b/src/main/java/haidnor/jvm/bcel/generic/DALOAD.java new file mode 100644 index 0000000..c0e0338 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DALOAD.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DALOAD - Load double from array + * + *
+ * Stack: ..., arrayref, index -> ..., result.word1, result.word2
+ * 
+ */ +public class DALOAD extends ArrayInstruction implements StackProducer { + + /** + * Load double from array + */ + public DALOAD() { + super(Const.DALOAD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitDALOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DASTORE.java b/src/main/java/haidnor/jvm/bcel/generic/DASTORE.java new file mode 100644 index 0000000..b7268eb --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DASTORE.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DASTORE - Store into double array + * + *
+ * Stack: ..., arrayref, index, value.word1, value.word2 -> ...
+ * 
+ */ +public class DASTORE extends ArrayInstruction implements StackConsumer { + + /** + * Store double into array + */ + public DASTORE() { + super(Const.DASTORE); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitDASTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DCMPG.java b/src/main/java/haidnor/jvm/bcel/generic/DCMPG.java new file mode 100644 index 0000000..63cb029 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DCMPG.java @@ -0,0 +1,55 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DCMPG - Compare doubles: value1 > value2 + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 -> ..., result
+ * 
+ */ +public class DCMPG extends Instruction implements TypedInstruction, StackProducer, StackConsumer { + + public DCMPG() { + super(Const.DCMPG, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitDCMPG(this); + } + + /** + * @return Type.DOUBLE + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.DOUBLE; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DCMPL.java b/src/main/java/haidnor/jvm/bcel/generic/DCMPL.java new file mode 100644 index 0000000..01a1fa8 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DCMPL.java @@ -0,0 +1,55 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DCMPL - Compare doubles: value1 < value2 + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 -> ..., result
+ * 
+ */ +public class DCMPL extends Instruction implements TypedInstruction, StackProducer, StackConsumer { + + public DCMPL() { + super(Const.DCMPL, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitDCMPL(this); + } + + /** + * @return Type.DOUBLE + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.DOUBLE; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DCONST.java b/src/main/java/haidnor/jvm/bcel/generic/DCONST.java new file mode 100644 index 0000000..46afc0a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DCONST.java @@ -0,0 +1,78 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DCONST - Push 0.0 or 1.0, other values cause an exception + * + *
+ * Stack: ... -> ...,
+ * 
+ */ +public class DCONST extends Instruction implements ConstantPushInstruction { + + private final double value; + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + DCONST() { + this(0); + } + + public DCONST(final double f) { + super(Const.DCONST_0, (short) 1); + if (f == 0.0) { + super.setOpcode(Const.DCONST_0); + } else if (f == 1.0) { + super.setOpcode(Const.DCONST_1); + } else { + throw new ClassGenException("DCONST can be used only for 0.0 and 1.0: " + f); + } + value = f; + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitPushInstruction(this); + v.visitStackProducer(this); + v.visitTypedInstruction(this); + v.visitConstantPushInstruction(this); + v.visitDCONST(this); + } + + /** + * @return Type.DOUBLE + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.DOUBLE; + } + + @Override + public Number getValue() { + return Double.valueOf(value); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DDIV.java b/src/main/java/haidnor/jvm/bcel/generic/DDIV.java new file mode 100644 index 0000000..fedb009 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DDIV.java @@ -0,0 +1,53 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DDIV - Divide doubles + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 ->
+ * 
+ * + * ..., result.word1, result.word2 + */ +public class DDIV extends ArithmeticInstruction { + + /** + * Divide doubles + */ + public DDIV() { + super(Const.DDIV); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitDDIV(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DLOAD.java b/src/main/java/haidnor/jvm/bcel/generic/DLOAD.java new file mode 100644 index 0000000..2799fd8 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DLOAD.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DLOAD - Load double from local variable + * + *
+ * Stack ... -> ..., result.word1, result.word2
+ * 
+ */ +public class DLOAD extends LoadInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + DLOAD() { + super(Const.DLOAD, Const.DLOAD_0); + } + + /** + * Load double from local variable + * + * @param n index of local variable + */ + public DLOAD(final int n) { + super(Const.DLOAD, Const.DLOAD_0, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + super.accept(v); + v.visitDLOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DMUL.java b/src/main/java/haidnor/jvm/bcel/generic/DMUL.java new file mode 100644 index 0000000..cc0b95f --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DMUL.java @@ -0,0 +1,53 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DMUL - Multiply doubles + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 ->
+ * 
+ * + * ..., result.word1, result.word2 + */ +public class DMUL extends ArithmeticInstruction { + + /** + * Multiply doubles + */ + public DMUL() { + super(Const.DMUL); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitDMUL(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DNEG.java b/src/main/java/haidnor/jvm/bcel/generic/DNEG.java new file mode 100644 index 0000000..ad23d9a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DNEG.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DNEG - Negate double + * + *
+ * Stack: ..., value.word1, value.word2 -> ..., result.word1, result.word2
+ * 
+ */ +public class DNEG extends ArithmeticInstruction { + + public DNEG() { + super(Const.DNEG); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitDNEG(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DREM.java b/src/main/java/haidnor/jvm/bcel/generic/DREM.java new file mode 100644 index 0000000..78c87a3 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DREM.java @@ -0,0 +1,53 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DREM - Remainder of doubles + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 ->
+ * 
+ * + * ..., result.word1, result.word2 + */ +public class DREM extends ArithmeticInstruction { + + /** + * Remainder of doubles + */ + public DREM() { + super(Const.DREM); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitDREM(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DRETURN.java b/src/main/java/haidnor/jvm/bcel/generic/DRETURN.java new file mode 100644 index 0000000..96e75b1 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DRETURN.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DRETURN - Return double from method + * + *
+ * Stack: ..., value.word1, value.word2 -> <empty>
+ * 
+ */ +public class DRETURN extends ReturnInstruction { + + /** + * Return double from method + */ + public DRETURN() { + super(Const.DRETURN); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackConsumer(this); + v.visitReturnInstruction(this); + v.visitDRETURN(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DSTORE.java b/src/main/java/haidnor/jvm/bcel/generic/DSTORE.java new file mode 100644 index 0000000..207128f --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DSTORE.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DSTORE - Store double into local variable + * + *
+ * Stack: ..., value.word1, value.word2 -> ...
+ * 
+ */ +public class DSTORE extends StoreInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + DSTORE() { + super(Const.DSTORE, Const.DSTORE_0); + } + + /** + * Store double into local variable + * + * @param n index of local variable + */ + public DSTORE(final int n) { + super(Const.DSTORE, Const.DSTORE_0, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + super.accept(v); + v.visitDSTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DSUB.java b/src/main/java/haidnor/jvm/bcel/generic/DSUB.java new file mode 100644 index 0000000..9742bb9 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DSUB.java @@ -0,0 +1,53 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DSUB - Substract doubles + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 ->
+ * 
+ * + * ..., result.word1, result.word2 + */ +public class DSUB extends ArithmeticInstruction { + + /** + * Substract doubles + */ + public DSUB() { + super(Const.DSUB); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitDSUB(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DUP.java b/src/main/java/haidnor/jvm/bcel/generic/DUP.java new file mode 100644 index 0000000..b723c6c --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DUP.java @@ -0,0 +1,47 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DUP - Duplicate top operand stack word + * + *
+ * Stack: ..., word -> ..., word, word
+ * 
+ */ +public class DUP extends StackInstruction implements PushInstruction { + + public DUP() { + super(Const.DUP); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitPushInstruction(this); + v.visitStackInstruction(this); + v.visitDUP(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DUP2.java b/src/main/java/haidnor/jvm/bcel/generic/DUP2.java new file mode 100644 index 0000000..7e1500b --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DUP2.java @@ -0,0 +1,47 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DUP2 - Duplicate two top operand stack words + * + *
+ * Stack: ..., word2, word1 -> ..., word2, word1, word2, word1
+ * 
+ */ +public class DUP2 extends StackInstruction implements PushInstruction { + + public DUP2() { + super(Const.DUP2); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitPushInstruction(this); + v.visitStackInstruction(this); + v.visitDUP2(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DUP2_X1.java b/src/main/java/haidnor/jvm/bcel/generic/DUP2_X1.java new file mode 100644 index 0000000..f6d5d9e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DUP2_X1.java @@ -0,0 +1,45 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DUP2_X1 - Duplicate two top operand stack words and put three down + * + *
+ * Stack: ..., word3, word2, word1 -> ..., word2, word1, word3, word2, word1
+ * 
+ */ +public class DUP2_X1 extends StackInstruction { + + public DUP2_X1() { + super(Const.DUP2_X1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackInstruction(this); + v.visitDUP2_X1(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DUP2_X2.java b/src/main/java/haidnor/jvm/bcel/generic/DUP2_X2.java new file mode 100644 index 0000000..e64ae87 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DUP2_X2.java @@ -0,0 +1,45 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DUP2_X2 - Duplicate two top operand stack words and put four down + * + *
+ * Stack: ..., word4, word3, word2, word1 -> ..., word2, word1, word4, word3, word2, word1
+ * 
+ */ +public class DUP2_X2 extends StackInstruction { + + public DUP2_X2() { + super(Const.DUP2_X2); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackInstruction(this); + v.visitDUP2_X2(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DUP_X1.java b/src/main/java/haidnor/jvm/bcel/generic/DUP_X1.java new file mode 100644 index 0000000..b9bc1bf --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DUP_X1.java @@ -0,0 +1,45 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DUP_X1 - Duplicate top operand stack word and put two down + * + *
+ * Stack: ..., word2, word1 -> ..., word1, word2, word1
+ * 
+ */ +public class DUP_X1 extends StackInstruction { + + public DUP_X1() { + super(Const.DUP_X1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackInstruction(this); + v.visitDUP_X1(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/DUP_X2.java b/src/main/java/haidnor/jvm/bcel/generic/DUP_X2.java new file mode 100644 index 0000000..d722e22 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/DUP_X2.java @@ -0,0 +1,45 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * DUP_X2 - Duplicate top operand stack word and put three down + * + *
+ * Stack: ..., word3, word2, word1 -> ..., word1, word3, word2, word1
+ * 
+ */ +public class DUP_X2 extends StackInstruction { + + public DUP_X2() { + super(Const.DUP_X2); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackInstruction(this); + v.visitDUP_X2(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ElementValueGen.java b/src/main/java/haidnor/jvm/bcel/generic/ElementValueGen.java new file mode 100644 index 0000000..489730b --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ElementValueGen.java @@ -0,0 +1,159 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.*; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.Deprecated; + +/** + * @since 6.0 + */ +public abstract class ElementValueGen { + public static final int STRING = 's'; + + public static final int ENUM_CONSTANT = 'e'; + + public static final int CLASS = 'c'; + + public static final int ANNOTATION = '@'; + + public static final int ARRAY = '['; + + public static final int PRIMITIVE_INT = 'I'; + + public static final int PRIMITIVE_BYTE = 'B'; + + public static final int PRIMITIVE_CHAR = 'C'; + + public static final int PRIMITIVE_DOUBLE = 'D'; + + public static final int PRIMITIVE_FLOAT = 'F'; + + public static final int PRIMITIVE_LONG = 'J'; + + public static final int PRIMITIVE_SHORT = 'S'; + + public static final int PRIMITIVE_BOOLEAN = 'Z'; + + /** + * Creates an (modifiable) ElementValueGen copy of an (immutable) ElementValue - constant pool is assumed correct. + */ + public static ElementValueGen copy(final ElementValue value, final ConstantPoolGen cpool, final boolean copyPoolEntries) { + switch (value.getElementValueType()) { + case 'B': // byte + case 'C': // char + case 'D': // double + case 'F': // float + case 'I': // int + case 'J': // long + case 'S': // short + case 'Z': // boolean + case 's': // String + return new SimpleElementValueGen((SimpleElementValue) value, cpool, copyPoolEntries); + case 'e': // Enum constant + return new EnumElementValueGen((EnumElementValue) value, cpool, copyPoolEntries); + case '@': // Annotation + return new AnnotationElementValueGen((AnnotationElementValue) value, cpool, copyPoolEntries); + case '[': // Array + return new ArrayElementValueGen((ArrayElementValue) value, cpool, copyPoolEntries); + case 'c': // Class + return new ClassElementValueGen((ClassElementValue) value, cpool, copyPoolEntries); + default: + throw new UnsupportedOperationException("Not implemented yet! (" + value.getElementValueType() + ")"); + } + } + + public static ElementValueGen readElementValue(final DataInput dis, final ConstantPoolGen cpGen) throws IOException { + final int type = dis.readUnsignedByte(); + switch (type) { + case 'B': // byte + return new SimpleElementValueGen(PRIMITIVE_BYTE, dis.readUnsignedShort(), cpGen); + case 'C': // char + return new SimpleElementValueGen(PRIMITIVE_CHAR, dis.readUnsignedShort(), cpGen); + case 'D': // double + return new SimpleElementValueGen(PRIMITIVE_DOUBLE, dis.readUnsignedShort(), cpGen); + case 'F': // float + return new SimpleElementValueGen(PRIMITIVE_FLOAT, dis.readUnsignedShort(), cpGen); + case 'I': // int + return new SimpleElementValueGen(PRIMITIVE_INT, dis.readUnsignedShort(), cpGen); + case 'J': // long + return new SimpleElementValueGen(PRIMITIVE_LONG, dis.readUnsignedShort(), cpGen); + case 'S': // short + return new SimpleElementValueGen(PRIMITIVE_SHORT, dis.readUnsignedShort(), cpGen); + case 'Z': // boolean + return new SimpleElementValueGen(PRIMITIVE_BOOLEAN, dis.readUnsignedShort(), cpGen); + case 's': // String + return new SimpleElementValueGen(STRING, dis.readUnsignedShort(), cpGen); + case 'e': // Enum constant + return new EnumElementValueGen(dis.readUnsignedShort(), dis.readUnsignedShort(), cpGen); + case 'c': // Class + return new ClassElementValueGen(dis.readUnsignedShort(), cpGen); + case '@': // Annotation + // TODO: isRuntimeVisible ?????????? + // FIXME + return new AnnotationElementValueGen(ANNOTATION, new AnnotationEntryGen(AnnotationEntry.read(dis, cpGen.getConstantPool(), true), cpGen, false), + cpGen); + case '[': // Array + final int numArrayVals = dis.readUnsignedShort(); + final ElementValue[] evalues = new ElementValue[numArrayVals]; + for (int j = 0; j < numArrayVals; j++) { + evalues[j] = ElementValue.readElementValue(dis, cpGen.getConstantPool()); + } + return new ArrayElementValueGen(ARRAY, evalues, cpGen); + default: + throw new IllegalArgumentException("Unexpected element value kind in annotation: " + type); + } + } + + /** + * @deprecated (since 6.0) will be made private and final; do not access directly, use getter + */ + @Deprecated + protected int type; + + /** + * @deprecated (since 6.0) will be made private and final; do not access directly, use getter + */ + @Deprecated + protected ConstantPoolGen cpGen; + + protected ElementValueGen(final int type, final ConstantPoolGen cpGen) { + this.type = type; + this.cpGen = cpGen; + } + + public abstract void dump(DataOutputStream dos) throws IOException; + + protected ConstantPoolGen getConstantPool() { + return cpGen; + } + + /** + * Subtypes return an immutable variant of the ElementValueGen + */ + public abstract ElementValue getElementValue(); + + public int getElementValueType() { + return type; + } + + public abstract String stringifyValue(); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ElementValuePairGen.java b/src/main/java/haidnor/jvm/bcel/generic/ElementValuePairGen.java new file mode 100644 index 0000000..a635a0f --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ElementValuePairGen.java @@ -0,0 +1,96 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.ConstantUtf8; +import haidnor.jvm.bcel.classfile.ElementValue; +import haidnor.jvm.bcel.classfile.ElementValuePair; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * @since 6.0 + */ +public class ElementValuePairGen { + private final int nameIdx; + + private final ElementValueGen value; + + private final ConstantPoolGen constantPoolGen; + + public ElementValuePairGen(final ElementValuePair nvp, final ConstantPoolGen cpool, final boolean copyPoolEntries) { + this.constantPoolGen = cpool; + // J5ASSERT: + // Could assert nvp.getNameString() points to the same thing as + // constantPoolGen.getConstant(nvp.getNameIndex()) + // if + // (!nvp.getNameString().equals(((ConstantUtf8)constantPoolGen.getConstant(nvp.getNameIndex())).getBytes())) + // { + // throw new IllegalArgumentException("envp buggered"); + // } + if (copyPoolEntries) { + nameIdx = cpool.addUtf8(nvp.getNameString()); + } else { + nameIdx = nvp.getNameIndex(); + } + value = ElementValueGen.copy(nvp.getValue(), cpool, copyPoolEntries); + } + + protected ElementValuePairGen(final int idx, final ElementValueGen value, final ConstantPoolGen cpool) { + this.nameIdx = idx; + this.value = value; + this.constantPoolGen = cpool; + } + + public ElementValuePairGen(final String name, final ElementValueGen value, final ConstantPoolGen cpool) { + this.nameIdx = cpool.addUtf8(name); + this.value = value; + this.constantPoolGen = cpool; + } + + protected void dump(final DataOutputStream dos) throws IOException { + dos.writeShort(nameIdx); // u2 name of the element + value.dump(dos); + } + + /** + * Retrieve an immutable version of this ElementNameValuePairGen + */ + public ElementValuePair getElementNameValuePair() { + final ElementValue immutableValue = value.getElementValue(); + return new ElementValuePair(nameIdx, immutableValue, constantPoolGen.getConstantPool()); + } + + public int getNameIndex() { + return nameIdx; + } + + public final String getNameString() { + // ConstantString cu8 = (ConstantString)constantPoolGen.getConstant(nameIdx); + return ((ConstantUtf8) constantPoolGen.getConstant(nameIdx)).getBytes(); + } + + public final ElementValueGen getValue() { + return value; + } + + @Override + public String toString() { + return "ElementValuePair:[" + getNameString() + "=" + value.stringifyValue() + "]"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/EmptyVisitor.java b/src/main/java/haidnor/jvm/bcel/generic/EmptyVisitor.java new file mode 100644 index 0000000..b52fdda --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/EmptyVisitor.java @@ -0,0 +1,750 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Supplies empty method bodies to be overridden by subclasses. + */ +public abstract class EmptyVisitor implements Visitor { + + @Override + public void visitAALOAD(final AALOAD obj) { + } + + @Override + public void visitAASTORE(final AASTORE obj) { + } + + @Override + public void visitACONST_NULL(final ACONST_NULL obj) { + } + + @Override + public void visitAllocationInstruction(final AllocationInstruction obj) { + } + + @Override + public void visitALOAD(final ALOAD obj) { + } + + @Override + public void visitANEWARRAY(final ANEWARRAY obj) { + } + + @Override + public void visitARETURN(final ARETURN obj) { + } + + @Override + public void visitArithmeticInstruction(final ArithmeticInstruction obj) { + } + + @Override + public void visitArrayInstruction(final ArrayInstruction obj) { + } + + @Override + public void visitARRAYLENGTH(final ARRAYLENGTH obj) { + } + + @Override + public void visitASTORE(final ASTORE obj) { + } + + @Override + public void visitATHROW(final ATHROW obj) { + } + + @Override + public void visitBALOAD(final BALOAD obj) { + } + + @Override + public void visitBASTORE(final BASTORE obj) { + } + + @Override + public void visitBIPUSH(final BIPUSH obj) { + } + + @Override + public void visitBranchInstruction(final BranchInstruction obj) { + } + + @Override + public void visitBREAKPOINT(final BREAKPOINT obj) { + } + + @Override + public void visitCALOAD(final CALOAD obj) { + } + + @Override + public void visitCASTORE(final CASTORE obj) { + } + + @Override + public void visitCHECKCAST(final CHECKCAST obj) { + } + + @Override + public void visitConstantPushInstruction(final ConstantPushInstruction obj) { + } + + @Override + public void visitConversionInstruction(final ConversionInstruction obj) { + } + + @Override + public void visitCPInstruction(final CPInstruction obj) { + } + + @Override + public void visitD2F(final D2F obj) { + } + + @Override + public void visitD2I(final D2I obj) { + } + + @Override + public void visitD2L(final D2L obj) { + } + + @Override + public void visitDADD(final DADD obj) { + } + + @Override + public void visitDALOAD(final DALOAD obj) { + } + + @Override + public void visitDASTORE(final DASTORE obj) { + } + + @Override + public void visitDCMPG(final DCMPG obj) { + } + + @Override + public void visitDCMPL(final DCMPL obj) { + } + + @Override + public void visitDCONST(final DCONST obj) { + } + + @Override + public void visitDDIV(final DDIV obj) { + } + + @Override + public void visitDLOAD(final DLOAD obj) { + } + + @Override + public void visitDMUL(final DMUL obj) { + } + + @Override + public void visitDNEG(final DNEG obj) { + } + + @Override + public void visitDREM(final DREM obj) { + } + + @Override + public void visitDRETURN(final DRETURN obj) { + } + + @Override + public void visitDSTORE(final DSTORE obj) { + } + + @Override + public void visitDSUB(final DSUB obj) { + } + + @Override + public void visitDUP(final DUP obj) { + } + + @Override + public void visitDUP_X1(final DUP_X1 obj) { + } + + @Override + public void visitDUP_X2(final DUP_X2 obj) { + } + + @Override + public void visitDUP2(final DUP2 obj) { + } + + @Override + public void visitDUP2_X1(final DUP2_X1 obj) { + } + + @Override + public void visitDUP2_X2(final DUP2_X2 obj) { + } + + @Override + public void visitExceptionThrower(final ExceptionThrower obj) { + } + + @Override + public void visitF2D(final F2D obj) { + } + + @Override + public void visitF2I(final F2I obj) { + } + + @Override + public void visitF2L(final F2L obj) { + } + + @Override + public void visitFADD(final FADD obj) { + } + + @Override + public void visitFALOAD(final FALOAD obj) { + } + + @Override + public void visitFASTORE(final FASTORE obj) { + } + + @Override + public void visitFCMPG(final FCMPG obj) { + } + + @Override + public void visitFCMPL(final FCMPL obj) { + } + + @Override + public void visitFCONST(final FCONST obj) { + } + + @Override + public void visitFDIV(final FDIV obj) { + } + + @Override + public void visitFieldInstruction(final FieldInstruction obj) { + } + + @Override + public void visitFieldOrMethod(final FieldOrMethod obj) { + } + + @Override + public void visitFLOAD(final FLOAD obj) { + } + + @Override + public void visitFMUL(final FMUL obj) { + } + + @Override + public void visitFNEG(final FNEG obj) { + } + + @Override + public void visitFREM(final FREM obj) { + } + + @Override + public void visitFRETURN(final FRETURN obj) { + } + + @Override + public void visitFSTORE(final FSTORE obj) { + } + + @Override + public void visitFSUB(final FSUB obj) { + } + + @Override + public void visitGETFIELD(final GETFIELD obj) { + } + + @Override + public void visitGETSTATIC(final GETSTATIC obj) { + } + + @Override + public void visitGOTO(final GOTO obj) { + } + + @Override + public void visitGOTO_W(final GOTO_W obj) { + } + + @Override + public void visitGotoInstruction(final GotoInstruction obj) { + } + + @Override + public void visitI2B(final I2B obj) { + } + + @Override + public void visitI2C(final I2C obj) { + } + + @Override + public void visitI2D(final I2D obj) { + } + + @Override + public void visitI2F(final I2F obj) { + } + + @Override + public void visitI2L(final I2L obj) { + } + + @Override + public void visitI2S(final I2S obj) { + } + + @Override + public void visitIADD(final IADD obj) { + } + + @Override + public void visitIALOAD(final IALOAD obj) { + } + + @Override + public void visitIAND(final IAND obj) { + } + + @Override + public void visitIASTORE(final IASTORE obj) { + } + + @Override + public void visitICONST(final ICONST obj) { + } + + @Override + public void visitIDIV(final IDIV obj) { + } + + @Override + public void visitIF_ACMPEQ(final IF_ACMPEQ obj) { + } + + @Override + public void visitIF_ACMPNE(final IF_ACMPNE obj) { + } + + @Override + public void visitIF_ICMPEQ(final IF_ICMPEQ obj) { + } + + @Override + public void visitIF_ICMPGE(final IF_ICMPGE obj) { + } + + @Override + public void visitIF_ICMPGT(final IF_ICMPGT obj) { + } + + @Override + public void visitIF_ICMPLE(final IF_ICMPLE obj) { + } + + @Override + public void visitIF_ICMPLT(final IF_ICMPLT obj) { + } + + @Override + public void visitIF_ICMPNE(final IF_ICMPNE obj) { + } + + @Override + public void visitIFEQ(final IFEQ obj) { + } + + @Override + public void visitIFGE(final IFGE obj) { + } + + @Override + public void visitIFGT(final IFGT obj) { + } + + @Override + public void visitIfInstruction(final IfInstruction obj) { + } + + @Override + public void visitIFLE(final IFLE obj) { + } + + @Override + public void visitIFLT(final IFLT obj) { + } + + @Override + public void visitIFNE(final IFNE obj) { + } + + @Override + public void visitIFNONNULL(final IFNONNULL obj) { + } + + @Override + public void visitIFNULL(final IFNULL obj) { + } + + @Override + public void visitIINC(final IINC obj) { + } + + @Override + public void visitILOAD(final ILOAD obj) { + } + + @Override + public void visitIMPDEP1(final IMPDEP1 obj) { + } + + @Override + public void visitIMPDEP2(final IMPDEP2 obj) { + } + + @Override + public void visitIMUL(final IMUL obj) { + } + + @Override + public void visitINEG(final INEG obj) { + } + + @Override + public void visitINSTANCEOF(final INSTANCEOF obj) { + } + + /** + * @since 6.0 + */ + @Override + public void visitINVOKEDYNAMIC(final INVOKEDYNAMIC obj) { + } + + @Override + public void visitInvokeInstruction(final InvokeInstruction obj) { + } + + @Override + public void visitINVOKEINTERFACE(final INVOKEINTERFACE obj) { + } + + @Override + public void visitINVOKESPECIAL(final INVOKESPECIAL obj) { + } + + @Override + public void visitINVOKESTATIC(final INVOKESTATIC obj) { + } + + @Override + public void visitINVOKEVIRTUAL(final INVOKEVIRTUAL obj) { + } + + @Override + public void visitIOR(final IOR obj) { + } + + @Override + public void visitIREM(final IREM obj) { + } + + @Override + public void visitIRETURN(final IRETURN obj) { + } + + @Override + public void visitISHL(final ISHL obj) { + } + + @Override + public void visitISHR(final ISHR obj) { + } + + @Override + public void visitISTORE(final ISTORE obj) { + } + + @Override + public void visitISUB(final ISUB obj) { + } + + @Override + public void visitIUSHR(final IUSHR obj) { + } + + @Override + public void visitIXOR(final IXOR obj) { + } + + @Override + public void visitJSR(final JSR obj) { + } + + @Override + public void visitJSR_W(final JSR_W obj) { + } + + @Override + public void visitJsrInstruction(final JsrInstruction obj) { + } + + @Override + public void visitL2D(final L2D obj) { + } + + @Override + public void visitL2F(final L2F obj) { + } + + @Override + public void visitL2I(final L2I obj) { + } + + @Override + public void visitLADD(final LADD obj) { + } + + @Override + public void visitLALOAD(final LALOAD obj) { + } + + @Override + public void visitLAND(final LAND obj) { + } + + @Override + public void visitLASTORE(final LASTORE obj) { + } + + @Override + public void visitLCMP(final LCMP obj) { + } + + @Override + public void visitLCONST(final LCONST obj) { + } + + @Override + public void visitLDC(final LDC obj) { + } + + @Override + public void visitLDC2_W(final LDC2_W obj) { + } + + @Override + public void visitLDIV(final LDIV obj) { + } + + @Override + public void visitLLOAD(final LLOAD obj) { + } + + @Override + public void visitLMUL(final LMUL obj) { + } + + @Override + public void visitLNEG(final LNEG obj) { + } + + @Override + public void visitLoadClass(final LoadClass obj) { + } + + @Override + public void visitLoadInstruction(final LoadInstruction obj) { + } + + @Override + public void visitLocalVariableInstruction(final LocalVariableInstruction obj) { + } + + @Override + public void visitLOOKUPSWITCH(final LOOKUPSWITCH obj) { + } + + @Override + public void visitLOR(final LOR obj) { + } + + @Override + public void visitLREM(final LREM obj) { + } + + @Override + public void visitLRETURN(final LRETURN obj) { + } + + @Override + public void visitLSHL(final LSHL obj) { + } + + @Override + public void visitLSHR(final LSHR obj) { + } + + @Override + public void visitLSTORE(final LSTORE obj) { + } + + @Override + public void visitLSUB(final LSUB obj) { + } + + @Override + public void visitLUSHR(final LUSHR obj) { + } + + @Override + public void visitLXOR(final LXOR obj) { + } + + @Override + public void visitMONITORENTER(final MONITORENTER obj) { + } + + @Override + public void visitMONITOREXIT(final MONITOREXIT obj) { + } + + @Override + public void visitMULTIANEWARRAY(final MULTIANEWARRAY obj) { + } + + @Override + public void visitNEW(final NEW obj) { + } + + @Override + public void visitNEWARRAY(final NEWARRAY obj) { + } + + @Override + public void visitNOP(final NOP obj) { + } + + @Override + public void visitPOP(final POP obj) { + } + + @Override + public void visitPOP2(final POP2 obj) { + } + + @Override + public void visitPopInstruction(final PopInstruction obj) { + } + + @Override + public void visitPushInstruction(final PushInstruction obj) { + } + + @Override + public void visitPUTFIELD(final PUTFIELD obj) { + } + + @Override + public void visitPUTSTATIC(final PUTSTATIC obj) { + } + + @Override + public void visitRET(final RET obj) { + } + + @Override + public void visitRETURN(final RETURN obj) { + } + + @Override + public void visitReturnInstruction(final ReturnInstruction obj) { + } + + @Override + public void visitSALOAD(final SALOAD obj) { + } + + @Override + public void visitSASTORE(final SASTORE obj) { + } + + @Override + public void visitSelect(final Select obj) { + } + + @Override + public void visitSIPUSH(final SIPUSH obj) { + } + + @Override + public void visitStackConsumer(final StackConsumer obj) { + } + + @Override + public void visitStackInstruction(final StackInstruction obj) { + } + + @Override + public void visitStackProducer(final StackProducer obj) { + } + + @Override + public void visitStoreInstruction(final StoreInstruction obj) { + } + + @Override + public void visitSWAP(final SWAP obj) { + } + + @Override + public void visitTABLESWITCH(final TABLESWITCH obj) { + } + + @Override + public void visitTypedInstruction(final TypedInstruction obj) { + } + + @Override + public void visitUnconditionalBranch(final UnconditionalBranch obj) { + } + + @Override + public void visitVariableLengthInstruction(final VariableLengthInstruction obj) { + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/EnumElementValueGen.java b/src/main/java/haidnor/jvm/bcel/generic/EnumElementValueGen.java new file mode 100644 index 0000000..87c6c67 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/EnumElementValueGen.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.ConstantUtf8; +import haidnor.jvm.bcel.classfile.ElementValue; +import haidnor.jvm.bcel.classfile.EnumElementValue; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * @since 6.0 + */ +public class EnumElementValueGen extends ElementValueGen { + // For enum types, these two indices point to the type and value + private final int typeIdx; + + private final int valueIdx; + + public EnumElementValueGen(final EnumElementValue value, final ConstantPoolGen cpool, final boolean copyPoolEntries) { + super(ENUM_CONSTANT, cpool); + if (copyPoolEntries) { + typeIdx = cpool.addUtf8(value.getEnumTypeString());// was + // addClass(value.getEnumTypeString()); + valueIdx = cpool.addUtf8(value.getEnumValueString()); // was + // addString(value.getEnumValueString()); + } else { + typeIdx = value.getTypeIndex(); + valueIdx = value.getValueIndex(); + } + } + + /** + * This ctor assumes the constant pool already contains the right type and value - as indicated by typeIdx and valueIdx. + * This ctor is used for deserialization + */ + protected EnumElementValueGen(final int typeIdx, final int valueIdx, final ConstantPoolGen cpool) { + super(ENUM_CONSTANT, cpool); + if (super.getElementValueType() != ENUM_CONSTANT) { + throw new IllegalArgumentException("Only element values of type enum can be built with this ctor - type specified: " + super.getElementValueType()); + } + this.typeIdx = typeIdx; + this.valueIdx = valueIdx; + } + + public EnumElementValueGen(final ObjectType t, final String value, final ConstantPoolGen cpool) { + super(ENUM_CONSTANT, cpool); + typeIdx = cpool.addUtf8(t.getSignature());// was addClass(t); + valueIdx = cpool.addUtf8(value);// was addString(value); + } + + @Override + public void dump(final DataOutputStream dos) throws IOException { + dos.writeByte(super.getElementValueType()); // u1 type of value (ENUM_CONSTANT == 'e') + dos.writeShort(typeIdx); // u2 + dos.writeShort(valueIdx); // u2 + } + + /** + * Return immutable variant of this EnumElementValue + */ + @Override + public ElementValue getElementValue() { + System.err.println("Duplicating value: " + getEnumTypeString() + ":" + getEnumValueString()); + return new EnumElementValue(super.getElementValueType(), typeIdx, valueIdx, getConstantPool().getConstantPool()); + } + + // BCELBUG: Should we need to call utility.signatureToString() on the output + // here? + public String getEnumTypeString() { + // Constant cc = getConstantPool().getConstant(typeIdx); + // ConstantClass cu8 = + // (ConstantClass)getConstantPool().getConstant(typeIdx); + // return + // ((ConstantUtf8)getConstantPool().getConstant(cu8.getNameIndex())).getBytes(); + return ((ConstantUtf8) getConstantPool().getConstant(typeIdx)).getBytes(); + // return Utility.signatureToString(cu8.getBytes()); + } + + public String getEnumValueString() { + return ((ConstantUtf8) getConstantPool().getConstant(valueIdx)).getBytes(); + // ConstantString cu8 = + // (ConstantString)getConstantPool().getConstant(valueIdx); + // return + // ((ConstantUtf8)getConstantPool().getConstant(cu8.getStringIndex())).getBytes(); + } + + public int getTypeIndex() { + return typeIdx; + } + + public int getValueIndex() { + return valueIdx; + } + + @Override + public String stringifyValue() { + final ConstantUtf8 cu8 = (ConstantUtf8) getConstantPool().getConstant(valueIdx); + return cu8.getBytes(); + // ConstantString cu8 = + // (ConstantString)getConstantPool().getConstant(valueIdx); + // return + // ((ConstantUtf8)getConstantPool().getConstant(cu8.getStringIndex())).getBytes(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ExceptionThrower.java b/src/main/java/haidnor/jvm/bcel/generic/ExceptionThrower.java new file mode 100644 index 0000000..923064e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ExceptionThrower.java @@ -0,0 +1,33 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denote an instruction that may throw a run-time or a linking exception (or both) during execution. This is not quite + * the truth as such; because all instructions may throw an java.lang.VirtualMachineError. These exceptions are omitted. + * + * The Lava Language Specification specifies exactly which RUN-TIME and which LINKING exceptions each + * instruction may throw which is reflected by the implementers. Due to the structure of the JVM specification, it may + * be possible that an Instruction implementing this interface returns a Class[] of size 0. + * + * Please note that we speak of an "exception" here when we mean any "Throwable" object; so this term is equally used + * for "Exception" and "Error" objects. + */ +public interface ExceptionThrower { + + Class[] getExceptions(); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/F2D.java b/src/main/java/haidnor/jvm/bcel/generic/F2D.java new file mode 100644 index 0000000..3e6f699 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/F2D.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * F2D - Convert float to double + * + *
+ * Stack: ..., value -> ..., result.word1, result.word2
+ * 
+ */ +public class F2D extends ConversionInstruction { + + /** + * Convert float to double + */ + public F2D() { + super(Const.F2D); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitF2D(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/F2I.java b/src/main/java/haidnor/jvm/bcel/generic/F2I.java new file mode 100644 index 0000000..effcddd --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/F2I.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * F2I - Convert float to int + * + *
+ * Stack: ..., value -> ..., result
+ * 
+ */ +public class F2I extends ConversionInstruction { + + /** + * Convert float to int + */ + public F2I() { + super(Const.F2I); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitF2I(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/F2L.java b/src/main/java/haidnor/jvm/bcel/generic/F2L.java new file mode 100644 index 0000000..8c6cd80 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/F2L.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * F2L - Convert float to long + * + *
+ * Stack: ..., value -> ..., result.word1, result.word2
+ * 
+ */ +public class F2L extends ConversionInstruction { + + /** + * Convert float to long + */ + public F2L() { + super(Const.F2L); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitF2L(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FADD.java b/src/main/java/haidnor/jvm/bcel/generic/FADD.java new file mode 100644 index 0000000..6b852ab --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FADD.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FADD - Add floats + * + *
+ * Stack: ..., value1, value2 -> result
+ * 
+ */ +public class FADD extends ArithmeticInstruction { + + /** + * Add floats + */ + public FADD() { + super(Const.FADD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitFADD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FALOAD.java b/src/main/java/haidnor/jvm/bcel/generic/FALOAD.java new file mode 100644 index 0000000..f1290ff --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FALOAD.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FALOAD - Load float from array + * + *
+ * Stack: ..., arrayref, index -> ..., value
+ * 
+ */ +public class FALOAD extends ArrayInstruction implements StackProducer { + + /** + * Load float from array + */ + public FALOAD() { + super(Const.FALOAD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitFALOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FASTORE.java b/src/main/java/haidnor/jvm/bcel/generic/FASTORE.java new file mode 100644 index 0000000..00e8c22 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FASTORE.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FASTORE - Store into float array + * + *
+ * Stack: ..., arrayref, index, value -> ...
+ * 
+ */ +public class FASTORE extends ArrayInstruction implements StackConsumer { + + /** + * Store float into array + */ + public FASTORE() { + super(Const.FASTORE); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitFASTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FCMPG.java b/src/main/java/haidnor/jvm/bcel/generic/FCMPG.java new file mode 100644 index 0000000..49ed63b --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FCMPG.java @@ -0,0 +1,55 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FCMPG - Compare floats: value1 > value2 + * + *
+ * Stack: ..., value1, value2 -> ..., result
+ * 
+ */ +public class FCMPG extends Instruction implements TypedInstruction, StackProducer, StackConsumer { + + public FCMPG() { + super(Const.FCMPG, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitFCMPG(this); + } + + /** + * @return Type.FLOAT + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.FLOAT; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FCMPL.java b/src/main/java/haidnor/jvm/bcel/generic/FCMPL.java new file mode 100644 index 0000000..66df719 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FCMPL.java @@ -0,0 +1,55 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FCMPL - Compare floats: value1 < value2 + * + *
+ * Stack: ..., value1, value2 -> ..., result
+ * 
+ */ +public class FCMPL extends Instruction implements TypedInstruction, StackProducer, StackConsumer { + + public FCMPL() { + super(Const.FCMPL, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitFCMPL(this); + } + + /** + * @return Type.FLOAT + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.FLOAT; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FCONST.java b/src/main/java/haidnor/jvm/bcel/generic/FCONST.java new file mode 100644 index 0000000..c4be825 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FCONST.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FCONST - Push 0.0, 1.0 or 2.0, other values cause an exception + * + *
+ * Stack: ... -> ...,
+ * 
+ */ +public class FCONST extends Instruction implements ConstantPushInstruction { + + private final float value; + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + FCONST() { + this(0); + } + + public FCONST(final float f) { + super(Const.FCONST_0, (short) 1); + if (f == 0.0) { + super.setOpcode(Const.FCONST_0); + } else if (f == 1.0) { + super.setOpcode(Const.FCONST_1); + } else if (f == 2.0) { + super.setOpcode(Const.FCONST_2); + } else { + throw new ClassGenException("FCONST can be used only for 0.0, 1.0 and 2.0: " + f); + } + value = f; + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitPushInstruction(this); + v.visitStackProducer(this); + v.visitTypedInstruction(this); + v.visitConstantPushInstruction(this); + v.visitFCONST(this); + } + + /** + * @return Type.FLOAT + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.FLOAT; + } + + @Override + public Number getValue() { + return Float.valueOf(value); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FDIV.java b/src/main/java/haidnor/jvm/bcel/generic/FDIV.java new file mode 100644 index 0000000..289148e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FDIV.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FDIV - Divide floats + * + *
+ * Stack: ..., value1, value2 -> result
+ * 
+ */ +public class FDIV extends ArithmeticInstruction { + + /** + * Divide floats + */ + public FDIV() { + super(Const.FDIV); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitFDIV(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FLOAD.java b/src/main/java/haidnor/jvm/bcel/generic/FLOAD.java new file mode 100644 index 0000000..6112ca6 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FLOAD.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FLOAD - Load float from local variable + * + *
+ * Stack ... -> ..., result
+ * 
+ */ +public class FLOAD extends LoadInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + FLOAD() { + super(Const.FLOAD, Const.FLOAD_0); + } + + /** + * Load float from local variable + * + * @param n index of local variable + */ + public FLOAD(final int n) { + super(Const.FLOAD, Const.FLOAD_0, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + super.accept(v); + v.visitFLOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FMUL.java b/src/main/java/haidnor/jvm/bcel/generic/FMUL.java new file mode 100644 index 0000000..6a157bf --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FMUL.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FMUL - Multiply floats + * + *
+ * Stack: ..., value1, value2 -> result
+ * 
+ */ +public class FMUL extends ArithmeticInstruction { + + /** + * Multiply floats + */ + public FMUL() { + super(Const.FMUL); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitFMUL(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FNEG.java b/src/main/java/haidnor/jvm/bcel/generic/FNEG.java new file mode 100644 index 0000000..ca33b6f --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FNEG.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FNEG - Negate float + * + *
+ * Stack: ..., value -> ..., result
+ * 
+ */ +public class FNEG extends ArithmeticInstruction { + + public FNEG() { + super(Const.FNEG); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitFNEG(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FREM.java b/src/main/java/haidnor/jvm/bcel/generic/FREM.java new file mode 100644 index 0000000..eb106b6 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FREM.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FREM - Remainder of floats + * + *
+ * Stack: ..., value1, value2 -> result
+ * 
+ */ +public class FREM extends ArithmeticInstruction { + + /** + * Remainder of floats + */ + public FREM() { + super(Const.FREM); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitFREM(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FRETURN.java b/src/main/java/haidnor/jvm/bcel/generic/FRETURN.java new file mode 100644 index 0000000..df3dece --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FRETURN.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FRETURN - Return float from method + * + *
+ * Stack: ..., value -> <empty>
+ * 
+ */ +public class FRETURN extends ReturnInstruction { + + /** + * Return float from method + */ + public FRETURN() { + super(Const.FRETURN); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackConsumer(this); + v.visitReturnInstruction(this); + v.visitFRETURN(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FSTORE.java b/src/main/java/haidnor/jvm/bcel/generic/FSTORE.java new file mode 100644 index 0000000..5eded70 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FSTORE.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FSTORE - Store float into local variable + * + *
+ * Stack: ..., value -> ...
+ * 
+ */ +public class FSTORE extends StoreInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + FSTORE() { + super(Const.FSTORE, Const.FSTORE_0); + } + + /** + * Store float into local variable + * + * @param n index of local variable + */ + public FSTORE(final int n) { + super(Const.FSTORE, Const.FSTORE_0, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + super.accept(v); + v.visitFSTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FSUB.java b/src/main/java/haidnor/jvm/bcel/generic/FSUB.java new file mode 100644 index 0000000..b355e74 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FSUB.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * FSUB - Substract floats + * + *
+ * Stack: ..., value1, value2 -> result
+ * 
+ */ +public class FSUB extends ArithmeticInstruction { + + /** + * Substract floats + */ + public FSUB() { + super(Const.FSUB); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitFSUB(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FieldGen.java b/src/main/java/haidnor/jvm/bcel/generic/FieldGen.java new file mode 100644 index 0000000..1e26b51 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FieldGen.java @@ -0,0 +1,336 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.*; +import haidnor.jvm.bcel.util.BCELComparator; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * Template class for building up a field. The only extraordinary thing one can do is to add a constant value attribute + * to a field (which must of course be compatible with to the declared type). + * + * @see JavaField + */ +public class FieldGen extends FieldGenOrMethodGen { + + private static BCELComparator bcelComparator = new BCELComparator() { + + @Override + public boolean equals(final Object o1, final Object o2) { + final FieldGen THIS = (FieldGen) o1; + final FieldGen THAT = (FieldGen) o2; + return Objects.equals(THIS.getName(), THAT.getName()) && Objects.equals(THIS.getSignature(), THAT.getSignature()); + } + + @Override + public int hashCode(final Object o) { + final FieldGen THIS = (FieldGen) o; + return THIS.getSignature().hashCode() ^ THIS.getName().hashCode(); + } + }; + + /** + * @return Comparison strategy object + */ + public static BCELComparator getComparator() { + return bcelComparator; + } + + /** + * @param comparator Comparison strategy object + */ + public static void setComparator(final BCELComparator comparator) { + bcelComparator = comparator; + } + + private Object value; + + private List observers; + + /** + * Instantiate from existing field. + * + * @param field Field object + * @param cp constant pool (must contain the same entries as the field's constant pool) + */ + public FieldGen(final JavaField field, final ConstantPoolGen cp) { + this(field.getAccessFlags(), Type.getType(field.getSignature()), field.getName(), cp); + final Attribute[] attrs = field.getAttributes(); + for (final Attribute attr : attrs) { + if (attr instanceof ConstantValue) { + setValue(((ConstantValue) attr).getConstantValueIndex()); + } else if (attr instanceof Annotations) { + final Annotations runtimeAnnotations = (Annotations) attr; + runtimeAnnotations.forEach(element -> addAnnotationEntry(new AnnotationEntryGen(element, cp, false))); + } else { + addAttribute(attr); + } + } + } + + /** + * Declare a field. If it is static (isStatic() == true) and has a basic type like int or String it may have an initial + * value associated with it as defined by setInitValue(). + * + * @param accessFlags access qualifiers + * @param type field type + * @param name field name + * @param cp constant pool + */ + public FieldGen(final int accessFlags, final Type type, final String name, final ConstantPoolGen cp) { + super(accessFlags); + setType(type); + setName(name); + setConstantPool(cp); + } + + private void addAnnotationsAsAttribute(final ConstantPoolGen cp) { + Stream.of(AnnotationEntryGen.getAnnotationAttributes(cp, super.getAnnotationEntries())).forEach(this::addAttribute); + } + + private int addConstant() { + switch (super.getType().getType()) { // sic + case Const.T_INT: + case Const.T_CHAR: + case Const.T_BYTE: + case Const.T_BOOLEAN: + case Const.T_SHORT: + return super.getConstantPool().addInteger(((Integer) value).intValue()); + case Const.T_FLOAT: + return super.getConstantPool().addFloat(((Float) value).floatValue()); + case Const.T_DOUBLE: + return super.getConstantPool().addDouble(((Double) value).doubleValue()); + case Const.T_LONG: + return super.getConstantPool().addLong(((Long) value).longValue()); + case Const.T_REFERENCE: + return super.getConstantPool().addString((String) value); + default: + throw new IllegalStateException("Unhandled : " + super.getType().getType()); // sic + } + } + + /** + * Add observer for this object. + */ + public void addObserver(final FieldObserver o) { + if (observers == null) { + observers = new ArrayList<>(); + } + observers.add(o); + } + + /** + * Remove any initial value. + */ + public void cancelInitValue() { + value = null; + } + + private void checkType(final Type atype) { + final Type superType = super.getType(); + if (superType == null) { + throw new ClassGenException("You haven't defined the type of the field yet"); + } + if (!isFinal()) { + throw new ClassGenException("Only final fields may have an initial value!"); + } + if (!superType.equals(atype)) { + throw new ClassGenException("Types are not compatible: " + superType + " vs. " + atype); + } + } + + /** + * @return deep copy of this field + */ + public FieldGen copy(final ConstantPoolGen cp) { + final FieldGen fg = (FieldGen) clone(); + fg.setConstantPool(cp); + return fg; + } + + /** + * Return value as defined by given BCELComparator strategy. By default two FieldGen objects are said to be equal when + * their names and signatures are equal. + * + * @see Object#equals(Object) + */ + @Override + public boolean equals(final Object obj) { + return bcelComparator.equals(this, obj); + } + + /** + * Get field object after having set up all necessary values. + */ + public JavaField getField() { + final String signature = getSignature(); + final int nameIndex = super.getConstantPool().addUtf8(super.getName()); + final int signatureIndex = super.getConstantPool().addUtf8(signature); + if (value != null) { + checkType(super.getType()); + final int index = addConstant(); + addAttribute(new ConstantValue(super.getConstantPool().addUtf8("ConstantValue"), 2, index, super.getConstantPool().getConstantPool())); // sic + } + addAnnotationsAsAttribute(super.getConstantPool()); + return new JavaField(super.getAccessFlags(), nameIndex, signatureIndex, getAttributes(), super.getConstantPool().getConstantPool()); // sic + } + + public String getInitValue() { + if (value != null) { + return value.toString(); + } + return null; + } + + @Override + public String getSignature() { + return super.getType().getSignature(); + } + + /** + * Return value as defined by given BCELComparator strategy. By default return the hashcode of the field's name XOR + * signature. + * + * @see Object#hashCode() + */ + @Override + public int hashCode() { + return bcelComparator.hashCode(this); + } + + /** + * Remove observer for this object. + */ + public void removeObserver(final FieldObserver o) { + if (observers != null) { + observers.remove(o); + } + } + + public void setInitValue(final boolean b) { + checkType(Type.BOOLEAN); + if (b) { + value = Integer.valueOf(1); + } + } + + public void setInitValue(final byte b) { + checkType(Type.BYTE); + if (b != 0) { + value = Integer.valueOf(b); + } + } + + public void setInitValue(final char c) { + checkType(Type.CHAR); + if (c != 0) { + value = Integer.valueOf(c); + } + } + + public void setInitValue(final double d) { + checkType(Type.DOUBLE); + if (d != 0.0) { + value = Double.valueOf(d); + } + } + + public void setInitValue(final float f) { + checkType(Type.FLOAT); + if (f != 0.0) { + value = Float.valueOf(f); + } + } + + public void setInitValue(final int i) { + checkType(Type.INT); + if (i != 0) { + value = Integer.valueOf(i); + } + } + + public void setInitValue(final long l) { + checkType(Type.LONG); + if (l != 0L) { + value = Long.valueOf(l); + } + } + + public void setInitValue(final short s) { + checkType(Type.SHORT); + if (s != 0) { + value = Integer.valueOf(s); + } + } + + /** + * Set (optional) initial value of field, otherwise it will be set to null/0/false by the JVM automatically. + */ + public void setInitValue(final String str) { + checkType(ObjectType.getInstance("java.lang.String")); + if (str != null) { + value = str; + } + } + + private void setValue(final int index) { + final ConstantPool cp = super.getConstantPool().getConstantPool(); + final Constant c = cp.getConstant(index); + value = ((ConstantObject) c).getConstantValue(cp); + } + + /** + * Return string representation close to declaration format, 'public static final short MAX = 100', e.g.. + * + * @return String representation of field + */ + @Override + public final String toString() { + String name; + String signature; + String access; // Short cuts to constant pool + access = Utility.accessToString(super.getAccessFlags()); + access = access.isEmpty() ? "" : access + " "; + signature = super.getType().toString(); + name = getName(); + final StringBuilder buf = new StringBuilder(32); // CHECKSTYLE IGNORE MagicNumber + buf.append(access).append(signature).append(" ").append(name); + final String value = getInitValue(); + if (value != null) { + buf.append(" = ").append(value); + } + return buf.toString(); + } + + /** + * Call notify() method on all observers. This method is not called automatically whenever the state has changed, but + * has to be called by the user after they have finished editing the object. + */ + public void update() { + if (observers != null) { + for (final FieldObserver observer : observers) { + observer.notify(this); + } + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FieldGenOrMethodGen.java b/src/main/java/haidnor/jvm/bcel/generic/FieldGenOrMethodGen.java new file mode 100644 index 0000000..4f2ba9d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FieldGenOrMethodGen.java @@ -0,0 +1,172 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.AccessFlags; +import haidnor.jvm.bcel.classfile.Attribute; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Super class for FieldGen and MethodGen objects, since they have some methods in common! + */ +public abstract class FieldGenOrMethodGen extends AccessFlags implements NamedAndTyped, Cloneable { + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected String name; + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected Type type; + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected ConstantPoolGen cp; + + private final List attributeList = new ArrayList<>(); + + // @since 6.0 + private final List annotationList = new ArrayList<>(); + + protected FieldGenOrMethodGen() { + } + + /** + * @since 6.0 + */ + protected FieldGenOrMethodGen(final int accessFlags) { // TODO could this be package protected? + super(accessFlags); + } + + protected void addAll(final Attribute[] attrs) { + Collections.addAll(attributeList, attrs); + } + + /** + * @since 6.0 + */ + public void addAnnotationEntry(final AnnotationEntryGen ag) { + annotationList.add(ag); + } + + /** + * Add an attribute to this method. Currently, the JVM knows about the 'Code', 'ConstantValue', 'Synthetic' and + * 'Exceptions' attributes. Other attributes will be ignored by the JVM but do no harm. + * + * @param a attribute to be added + */ + public void addAttribute(final Attribute a) { + attributeList.add(a); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (final CloneNotSupportedException e) { + throw new Error("Clone Not Supported"); // never happens + } + } + + public AnnotationEntryGen[] getAnnotationEntries() { + return annotationList.toArray(AnnotationEntryGen.EMPTY_ARRAY); + } + + /** + * @return all attributes of this method. + */ + public Attribute[] getAttributes() { + return attributeList.toArray(Attribute.EMPTY_ARRAY); + } + + public ConstantPoolGen getConstantPool() { + return cp; + } + + /** + * @return name of method/field. + */ + @Override + public String getName() { + return name; + } + + /** + * @return signature of method/field. + */ + public abstract String getSignature(); + + @Override + public Type getType() { + return type; + } + + /** + * @since 6.0 + */ + public void removeAnnotationEntries() { + annotationList.clear(); + } + + /** + * @since 6.0 + */ + public void removeAnnotationEntry(final AnnotationEntryGen ag) { + annotationList.remove(ag); + } + + /** + * Remove an attribute. + */ + public void removeAttribute(final Attribute a) { + attributeList.remove(a); + } + + /** + * Remove all attributes. + */ + public void removeAttributes() { + attributeList.clear(); + } + + public void setConstantPool(final ConstantPoolGen cp) { // TODO could be package-protected? + this.cp = cp; + } + + @Override + public void setName(final String name) { // TODO could be package-protected? + this.name = name; + } + + @Override + public void setType(final Type type) { // TODO could be package-protected? + if (type.getType() == Const.T_ADDRESS) { + throw new IllegalArgumentException("Type can not be " + type); + } + this.type = type; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FieldInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/FieldInstruction.java new file mode 100644 index 0000000..c899175 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FieldInstruction.java @@ -0,0 +1,76 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.ConstantPool; + +/** + * Super class for the GET/PUTxxx family of instructions. + */ +public abstract class FieldInstruction extends FieldOrMethod { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + FieldInstruction() { + } + + /** + * @param index to constant pool + */ + protected FieldInstruction(final short opcode, final int index) { + super(opcode, index); + } + + /** + * @return name of referenced field. + */ + public String getFieldName(final ConstantPoolGen cpg) { + return getName(cpg); + } + + /** + * @return size of field (1 or 2) + */ + protected int getFieldSize(final ConstantPoolGen cpg) { + return Type.size(Type.getTypeSize(getSignature(cpg))); + } + + /** + * @return type of field + */ + public Type getFieldType(final ConstantPoolGen cpg) { + return Type.getType(getSignature(cpg)); + } + + /** + * @return return type of referenced field + */ + @Override + public Type getType(final ConstantPoolGen cpg) { + return getFieldType(cpg); + } + + /** + * @return mnemonic for instruction with symbolic references resolved + */ + @Override + public String toString(final ConstantPool cp) { + return Const.getOpcodeName(super.getOpcode()) + " " + cp.constantToString(super.getIndex(), Const.CONSTANT_Fieldref); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FieldObserver.java b/src/main/java/haidnor/jvm/bcel/generic/FieldObserver.java new file mode 100644 index 0000000..b60793d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FieldObserver.java @@ -0,0 +1,26 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Imnplement this interface if you're interested in changes to a FieldGen object and register yourself with + * addObserver(). + */ +public interface FieldObserver { + + void notify(FieldGen field); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/FieldOrMethod.java b/src/main/java/haidnor/jvm/bcel/generic/FieldOrMethod.java new file mode 100644 index 0000000..b0fc8ac --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/FieldOrMethod.java @@ -0,0 +1,125 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.*; + +import java.lang.Deprecated; + +/** + * Super class for InvokeInstruction and FieldInstruction, since they have some methods in common! + */ +public abstract class FieldOrMethod extends CPInstruction implements LoadClass { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + FieldOrMethod() { + // no init + } + + /** + * @param index to constant pool + */ + protected FieldOrMethod(final short opcode, final int index) { + super(opcode, index); + } + + /** + * @return name of the referenced class/interface + * @deprecated If the instruction references an array class, this method will return "java.lang.Object". For code + * generated by Java 1.5, this answer is sometimes wrong (e.g., if the "clone()" method is called on an + * array). A better idea is to use the {@link #getReferenceType(ConstantPoolGen)} method, which correctly + * distinguishes between class types and array types. + * + */ + @Deprecated + public String getClassName(final ConstantPoolGen cpg) { + final ConstantPool cp = cpg.getConstantPool(); + final ConstantCP cmr = (ConstantCP) cp.getConstant(super.getIndex()); + final String className = cp.getConstantString(cmr.getClassIndex(), Const.CONSTANT_Class); + if (className.startsWith("[")) { + // Turn array classes into java.lang.Object. + return "java.lang.Object"; + } + return Utility.pathToPackage(className); + } + + /** + * @return type of the referenced class/interface + * @deprecated If the instruction references an array class, the ObjectType returned will be invalid. Use + * getReferenceType() instead. + */ + @Deprecated + public ObjectType getClassType(final ConstantPoolGen cpg) { + return ObjectType.getInstance(getClassName(cpg)); + } + + /** + * Gets the ObjectType of the method return or field. + * + * @return type of the referenced class/interface + * @throws ClassGenException when the field is (or method returns) an array, + */ + @Override + public ObjectType getLoadClassType(final ConstantPoolGen cpg) { + final ReferenceType rt = getReferenceType(cpg); + if (rt instanceof ObjectType) { + return (ObjectType) rt; + } + throw new ClassGenException(rt.getClass().getCanonicalName() + " " + rt.getSignature() + " does not represent an ObjectType"); + } + + /** + * @return name of referenced method/field. + */ + public String getName(final ConstantPoolGen cpg) { + final ConstantPool cp = cpg.getConstantPool(); + final ConstantCP cmr = (ConstantCP) cp.getConstant(super.getIndex()); + final ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(cmr.getNameAndTypeIndex()); + return ((ConstantUtf8) cp.getConstant(cnat.getNameIndex())).getBytes(); + } + + /** + * Gets the reference type representing the class, interface, or array class referenced by the instruction. + * + * @param cpg the ConstantPoolGen used to create the instruction + * @return an ObjectType (if the referenced class type is a class or interface), or an ArrayType (if the referenced + * class type is an array class) + */ + public ReferenceType getReferenceType(final ConstantPoolGen cpg) { + final ConstantPool cp = cpg.getConstantPool(); + final ConstantCP cmr = (ConstantCP) cp.getConstant(super.getIndex()); + String className = cp.getConstantString(cmr.getClassIndex(), Const.CONSTANT_Class); + if (className.startsWith("[")) { + return (ArrayType) Type.getType(className); + } + className = Utility.pathToPackage(className); + return ObjectType.getInstance(className); + } + + /** + * @return signature of referenced method/field. + */ + public String getSignature(final ConstantPoolGen cpg) { + final ConstantPool cp = cpg.getConstantPool(); + final ConstantCP cmr = (ConstantCP) cp.getConstant(super.getIndex()); + final ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(cmr.getNameAndTypeIndex()); + return ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/GETFIELD.java b/src/main/java/haidnor/jvm/bcel/generic/GETFIELD.java new file mode 100644 index 0000000..0df16c2 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/GETFIELD.java @@ -0,0 +1,76 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * GETFIELD - Fetch field from object + * + *
+ * Stack: ..., objectref -> ..., value
+ * 
+ * + * OR + * + *
+ * Stack: ..., objectref -> ..., value.word1, value.word2
+ * 
+ */ +public class GETFIELD extends FieldInstruction implements ExceptionThrower, StackConsumer, StackProducer { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + GETFIELD() { + } + + public GETFIELD(final int index) { + super(Const.GETFIELD, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitStackConsumer(this); + v.visitStackProducer(this); + v.visitTypedInstruction(this); + v.visitLoadClass(this); + v.visitCPInstruction(this); + v.visitFieldOrMethod(this); + v.visitFieldInstruction(this); + v.visitGETFIELD(this); + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_FIELD_AND_METHOD_RESOLUTION, ExceptionConst.NULL_POINTER_EXCEPTION, + ExceptionConst.INCOMPATIBLE_CLASS_CHANGE_ERROR); + } + + @Override + public int produceStack(final ConstantPoolGen cpg) { + return getFieldSize(cpg); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/GETSTATIC.java b/src/main/java/haidnor/jvm/bcel/generic/GETSTATIC.java new file mode 100644 index 0000000..699b656 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/GETSTATIC.java @@ -0,0 +1,75 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * GETSTATIC - Fetch static field from class + * + *
+ * Stack: ..., -> ..., value
+ * 
+ * + * OR + * + *
+ * Stack: ..., -> ..., value.word1, value.word2
+ * 
+ */ +public class GETSTATIC extends FieldInstruction implements PushInstruction, ExceptionThrower { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + GETSTATIC() { + } + + public GETSTATIC(final int index) { + super(Const.GETSTATIC, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitPushInstruction(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitLoadClass(this); + v.visitCPInstruction(this); + v.visitFieldOrMethod(this); + v.visitFieldInstruction(this); + v.visitGETSTATIC(this); + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_FIELD_AND_METHOD_RESOLUTION, ExceptionConst.INCOMPATIBLE_CLASS_CHANGE_ERROR); + } + + @Override + public int produceStack(final ConstantPoolGen cpg) { + return getFieldSize(cpg); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/GOTO.java b/src/main/java/haidnor/jvm/bcel/generic/GOTO.java new file mode 100644 index 0000000..76b117b --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/GOTO.java @@ -0,0 +1,92 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * GOTO - Branch always (to relative offset, not absolute address) + */ +public class GOTO extends GotoInstruction implements VariableLengthInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + GOTO() { + } + + public GOTO(final InstructionHandle target) { + super(Const.GOTO, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitVariableLengthInstruction(this); + v.visitUnconditionalBranch(this); + v.visitBranchInstruction(this); + v.visitGotoInstruction(this); + v.visitGOTO(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + super.setIndex(getTargetOffset()); + final short opcode = getOpcode(); + if (opcode == Const.GOTO) { + super.dump(out); + } else { // GOTO_W + super.setIndex(getTargetOffset()); + out.writeByte(opcode); + out.writeInt(super.getIndex()); + } + } + + /** + * Called in pass 2 of InstructionList.setPositions() in order to update the branch target, that may shift due to + * variable length instructions. + * + * @param offset additional offset caused by preceding (variable length) instructions + * @param maxOffset the maximum offset that may be caused by these instructions + * @return additional offset caused by possible change of this instruction's length + */ + @Override + protected int updatePosition(final int offset, final int maxOffset) { + final int i = getTargetOffset(); // Depending on old position value + setPosition(getPosition() + offset); // Position may be shifted by preceding expansions + if (Math.abs(i) >= Short.MAX_VALUE - maxOffset) { // to large for short (estimate) + super.setOpcode(Const.GOTO_W); + final short oldLength = (short) super.getLength(); + super.setLength(5); + return super.getLength() - oldLength; + } + return 0; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/GOTO_W.java b/src/main/java/haidnor/jvm/bcel/generic/GOTO_W.java new file mode 100644 index 0000000..28df754 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/GOTO_W.java @@ -0,0 +1,75 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * GOTO_W - Branch always (to relative offset, not absolute address) + */ +public class GOTO_W extends GotoInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + GOTO_W() { + } + + public GOTO_W(final InstructionHandle target) { + super(Const.GOTO_W, target); + super.setLength(5); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitUnconditionalBranch(this); + v.visitBranchInstruction(this); + v.visitGotoInstruction(this); + v.visitGOTO_W(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + super.setIndex(getTargetOffset()); + out.writeByte(super.getOpcode()); + out.writeInt(super.getIndex()); + } + + /** + * Read needed data (e.g. index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + super.setIndex(bytes.readInt()); + super.setLength(5); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/GotoInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/GotoInstruction.java new file mode 100644 index 0000000..f438213 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/GotoInstruction.java @@ -0,0 +1,33 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Super class for GOTO + */ +public abstract class GotoInstruction extends BranchInstruction implements UnconditionalBranch { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + GotoInstruction() { + } + + GotoInstruction(final short opcode, final InstructionHandle target) { + super(opcode, target); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/I2B.java b/src/main/java/haidnor/jvm/bcel/generic/I2B.java new file mode 100644 index 0000000..908d434 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/I2B.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * I2B - Convert int to byte + * + *
+ * Stack: ..., value -> ..., result
+ * 
+ */ +public class I2B extends ConversionInstruction { + + /** + * Convert int to byte + */ + public I2B() { + super(Const.I2B); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitI2B(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/I2C.java b/src/main/java/haidnor/jvm/bcel/generic/I2C.java new file mode 100644 index 0000000..fb11dc6 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/I2C.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * I2C - Convert int to char + * + *
+ * Stack: ..., value -> ..., result
+ * 
+ */ +public class I2C extends ConversionInstruction { + + /** + * Convert int to char + */ + public I2C() { + super(Const.I2C); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitI2C(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/I2D.java b/src/main/java/haidnor/jvm/bcel/generic/I2D.java new file mode 100644 index 0000000..b09a562 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/I2D.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * I2D - Convert int to double + * + *
+ * Stack: ..., value -> ..., result.word1, result.word2
+ * 
+ */ +public class I2D extends ConversionInstruction { + + /** + * Convert int to double + */ + public I2D() { + super(Const.I2D); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitI2D(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/I2F.java b/src/main/java/haidnor/jvm/bcel/generic/I2F.java new file mode 100644 index 0000000..1ae1a0f --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/I2F.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * I2F - Convert int to float + * + *
+ * Stack: ..., value -> ..., result
+ * 
+ */ +public class I2F extends ConversionInstruction { + + /** + * Convert int to float + */ + public I2F() { + super(Const.I2F); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitI2F(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/I2L.java b/src/main/java/haidnor/jvm/bcel/generic/I2L.java new file mode 100644 index 0000000..ce04421 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/I2L.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * I2L - Convert int to long + * + *
+ * Stack: ..., value -> ..., result.word1, result.word2
+ * 
+ */ +public class I2L extends ConversionInstruction { + + /** + * Convert int to long + */ + public I2L() { + super(Const.I2L); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitI2L(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/I2S.java b/src/main/java/haidnor/jvm/bcel/generic/I2S.java new file mode 100644 index 0000000..a5edbd7 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/I2S.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * I2S - Convert int to short + * + *
+ * Stack: ..., value -> ..., result
+ * 
+ */ +public class I2S extends ConversionInstruction { + + public I2S() { + super(Const.I2S); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitI2S(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IADD.java b/src/main/java/haidnor/jvm/bcel/generic/IADD.java new file mode 100644 index 0000000..e83f254 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IADD.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IADD - Add ints + * + *
+ * Stack: ..., value1, value2 -> result
+ * 
+ */ +public class IADD extends ArithmeticInstruction { + + /** + * Add ints + */ + public IADD() { + super(Const.IADD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitIADD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IALOAD.java b/src/main/java/haidnor/jvm/bcel/generic/IALOAD.java new file mode 100644 index 0000000..4abed2e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IALOAD.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IALOAD - Load int from array + * + *
+ * Stack: ..., arrayref, index -> ..., value
+ * 
+ */ +public class IALOAD extends ArrayInstruction implements StackProducer { + + /** + * Load int from array + */ + public IALOAD() { + super(Const.IALOAD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitIALOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IAND.java b/src/main/java/haidnor/jvm/bcel/generic/IAND.java new file mode 100644 index 0000000..5526cdf --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IAND.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IAND - Bitwise AND int + * + *
+ * Stack: ..., value1, value2 -> ..., result
+ * 
+ */ +public class IAND extends ArithmeticInstruction { + + public IAND() { + super(Const.IAND); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitIAND(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IASTORE.java b/src/main/java/haidnor/jvm/bcel/generic/IASTORE.java new file mode 100644 index 0000000..6eda48a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IASTORE.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IASTORE - Store into int array + * + *
+ * Stack: ..., arrayref, index, value -> ...
+ * 
+ */ +public class IASTORE extends ArrayInstruction implements StackConsumer { + + /** + * Store into int array + */ + public IASTORE() { + super(Const.IASTORE); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitIASTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ICONST.java b/src/main/java/haidnor/jvm/bcel/generic/ICONST.java new file mode 100644 index 0000000..c2fdd07 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ICONST.java @@ -0,0 +1,75 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * ICONST - Push value between -1, ..., 5, other values cause an exception + * + *
+ * Stack: ... -> ...,
+ * 
+ */ +public class ICONST extends Instruction implements ConstantPushInstruction { + + private final int value; + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + ICONST() { + this(0); + } + + public ICONST(final int i) { + super(Const.ICONST_0, (short) 1); + if (i < -1 || i > 5) { + throw new ClassGenException("ICONST can be used only for value between -1 and 5: " + i); + } + super.setOpcode((short) (Const.ICONST_0 + i)); // Even works for i == -1 + value = i; + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitPushInstruction(this); + v.visitStackProducer(this); + v.visitTypedInstruction(this); + v.visitConstantPushInstruction(this); + v.visitICONST(this); + } + + /** + * @return Type.INT + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.INT; + } + + @Override + public Number getValue() { + return Integer.valueOf(value); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IDIV.java b/src/main/java/haidnor/jvm/bcel/generic/IDIV.java new file mode 100644 index 0000000..a3806b7 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IDIV.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * IDIV - Divide ints + * + *
+ * Stack: ..., value1, value2 -> result
+ * 
+ */ +public class IDIV extends ArithmeticInstruction implements ExceptionThrower { + + /** + * Divide ints + */ + public IDIV() { + super(Const.IDIV); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitIDIV(this); + } + + /** + * @return exceptions this instruction may cause + */ + @Override + public Class[] getExceptions() { + return new Class[] {ExceptionConst.ARITHMETIC_EXCEPTION}; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IFEQ.java b/src/main/java/haidnor/jvm/bcel/generic/IFEQ.java new file mode 100644 index 0000000..4118ac9 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IFEQ.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IFEQ - Branch if int comparison with zero succeeds + * + *
+ * Stack: ..., value -> ...
+ * 
+ */ +public class IFEQ extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IFEQ() { + } + + public IFEQ(final InstructionHandle target) { + super(Const.IFEQ, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIFEQ(this); + } + + /** + * @return negation of instruction, e.g. IFEQ.negate() == IFNE + */ + @Override + public IfInstruction negate() { + return new IFNE(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IFGE.java b/src/main/java/haidnor/jvm/bcel/generic/IFGE.java new file mode 100644 index 0000000..a6d52ba --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IFGE.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IFGE - Branch if int comparison with zero succeeds + * + *
+ * Stack: ..., value -> ...
+ * 
+ */ +public class IFGE extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IFGE() { + } + + public IFGE(final InstructionHandle target) { + super(Const.IFGE, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIFGE(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IFLT(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IFGT.java b/src/main/java/haidnor/jvm/bcel/generic/IFGT.java new file mode 100644 index 0000000..ec13798 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IFGT.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IFGT - Branch if int comparison with zero succeeds + * + *
+ * Stack: ..., value -> ...
+ * 
+ */ +public class IFGT extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IFGT() { + } + + public IFGT(final InstructionHandle target) { + super(Const.IFGT, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIFGT(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IFLE(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IFLE.java b/src/main/java/haidnor/jvm/bcel/generic/IFLE.java new file mode 100644 index 0000000..cff943b --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IFLE.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IFLE - Branch if int comparison with zero succeeds + * + *
+ * Stack: ..., value -> ...
+ * 
+ */ +public class IFLE extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IFLE() { + } + + public IFLE(final InstructionHandle target) { + super(Const.IFLE, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIFLE(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IFGT(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IFLT.java b/src/main/java/haidnor/jvm/bcel/generic/IFLT.java new file mode 100644 index 0000000..82ff8be --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IFLT.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IFLT - Branch if int comparison with zero succeeds + * + *
+ * Stack: ..., value -> ...
+ * 
+ */ +public class IFLT extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IFLT() { + } + + public IFLT(final InstructionHandle target) { + super(Const.IFLT, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIFLT(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IFGE(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IFNE.java b/src/main/java/haidnor/jvm/bcel/generic/IFNE.java new file mode 100644 index 0000000..90855c9 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IFNE.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IFNE - Branch if int comparison with zero succeeds + * + *
+ * Stack: ..., value -> ...
+ * 
+ */ +public class IFNE extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IFNE() { + } + + public IFNE(final InstructionHandle target) { + super(Const.IFNE, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIFNE(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IFEQ(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IFNONNULL.java b/src/main/java/haidnor/jvm/bcel/generic/IFNONNULL.java new file mode 100644 index 0000000..c4df443 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IFNONNULL.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IFNONNULL - Branch if reference is not null + * + *
+ * Stack: ..., reference -> ...
+ * 
+ */ +public class IFNONNULL extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IFNONNULL() { + } + + public IFNONNULL(final InstructionHandle target) { + super(Const.IFNONNULL, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIFNONNULL(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IFNULL(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IFNULL.java b/src/main/java/haidnor/jvm/bcel/generic/IFNULL.java new file mode 100644 index 0000000..1bab57d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IFNULL.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IFNULL - Branch if reference is not null + * + *
+ * Stack: ..., reference -> ...
+ * 
+ */ +public class IFNULL extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IFNULL() { + } + + public IFNULL(final InstructionHandle target) { + super(Const.IFNULL, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIFNULL(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IFNONNULL(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IF_ACMPEQ.java b/src/main/java/haidnor/jvm/bcel/generic/IF_ACMPEQ.java new file mode 100644 index 0000000..93eec04 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IF_ACMPEQ.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IF_ACMPEQ - Branch if reference comparison succeeds + * + *
+ * Stack: ..., value1, value2 -> ...
+ * 
+ */ +public class IF_ACMPEQ extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IF_ACMPEQ() { + } + + public IF_ACMPEQ(final InstructionHandle target) { + super(Const.IF_ACMPEQ, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIF_ACMPEQ(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IF_ACMPNE(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IF_ACMPNE.java b/src/main/java/haidnor/jvm/bcel/generic/IF_ACMPNE.java new file mode 100644 index 0000000..c8761fe --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IF_ACMPNE.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IF_ACMPNE - Branch if reference comparison doesn't succeed + * + *
+ * Stack: ..., value1, value2 -> ...
+ * 
+ */ +public class IF_ACMPNE extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IF_ACMPNE() { + } + + public IF_ACMPNE(final InstructionHandle target) { + super(Const.IF_ACMPNE, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIF_ACMPNE(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IF_ACMPEQ(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPEQ.java b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPEQ.java new file mode 100644 index 0000000..3f8c95e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPEQ.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IF_ICMPEQ - Branch if int comparison succeeds + * + *
+ * Stack: ..., value1, value2 -> ...
+ * 
+ */ +public class IF_ICMPEQ extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IF_ICMPEQ() { + } + + public IF_ICMPEQ(final InstructionHandle target) { + super(Const.IF_ICMPEQ, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIF_ICMPEQ(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IF_ICMPNE(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPGE.java b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPGE.java new file mode 100644 index 0000000..c4fad79 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPGE.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IF_ICMPGE - Branch if int comparison succeeds + * + *
+ * Stack: ..., value1, value2 -> ...
+ * 
+ */ +public class IF_ICMPGE extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IF_ICMPGE() { + } + + public IF_ICMPGE(final InstructionHandle target) { + super(Const.IF_ICMPGE, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIF_ICMPGE(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IF_ICMPLT(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPGT.java b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPGT.java new file mode 100644 index 0000000..c11ae2a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPGT.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IF_ICMPGT - Branch if int comparison succeeds + * + *
+ * Stack: ..., value1, value2 -> ...
+ * 
+ */ +public class IF_ICMPGT extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IF_ICMPGT() { + } + + public IF_ICMPGT(final InstructionHandle target) { + super(Const.IF_ICMPGT, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIF_ICMPGT(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IF_ICMPLE(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPLE.java b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPLE.java new file mode 100644 index 0000000..f6e259c --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPLE.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IF_ICMPLE - Branch if int comparison succeeds + * + *
+ * Stack: ..., value1, value2 -> ...
+ * 
+ */ +public class IF_ICMPLE extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IF_ICMPLE() { + } + + public IF_ICMPLE(final InstructionHandle target) { + super(Const.IF_ICMPLE, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIF_ICMPLE(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IF_ICMPGT(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPLT.java b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPLT.java new file mode 100644 index 0000000..339e686 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPLT.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IF_ICMPLT - Branch if int comparison succeeds + * + *
+ * Stack: ..., value1, value2 -> ...
+ * 
+ */ +public class IF_ICMPLT extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IF_ICMPLT() { + } + + public IF_ICMPLT(final InstructionHandle target) { + super(Const.IF_ICMPLT, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIF_ICMPLT(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IF_ICMPGE(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPNE.java b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPNE.java new file mode 100644 index 0000000..fb32a7e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IF_ICMPNE.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IF_ICMPNE - Branch if int comparison doesn't succeed + * + *
+ * Stack: ..., value1, value2 -> ...
+ * 
+ */ +public class IF_ICMPNE extends IfInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IF_ICMPNE() { + } + + public IF_ICMPNE(final InstructionHandle target) { + super(Const.IF_ICMPNE, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitIfInstruction(this); + v.visitIF_ICMPNE(this); + } + + /** + * @return negation of instruction + */ + @Override + public IfInstruction negate() { + return new IF_ICMPEQ(super.getTarget()); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IINC.java b/src/main/java/haidnor/jvm/bcel/generic/IINC.java new file mode 100644 index 0000000..8ea16d4 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IINC.java @@ -0,0 +1,158 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * IINC - Increment local variable by constant + */ +public class IINC extends LocalVariableInstruction { + + private boolean wide; + private int c; + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IINC() { + } + + /** + * @param n index of local variable + * @param c increment factor + */ + public IINC(final int n, final int c) { + // Default behavior of LocalVariableInstruction causes error + super.setOpcode(Const.IINC); + super.setLength((short) 3); + setIndex(n); // May set wide as side effect + setIncrement(c); + } + + /** + * Calls corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitLocalVariableInstruction(this); + v.visitIINC(this); + } + + /** + * Dumps instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + if (wide) { + out.writeByte(Const.WIDE); + } + out.writeByte(super.getOpcode()); + if (wide) { + out.writeShort(super.getIndex()); + out.writeShort(c); + } else { + out.writeByte(super.getIndex()); + out.writeByte(c); + } + } + + /** + * @return increment factor + */ + public final int getIncrement() { + return c; + } + + /** + * @return int type + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.INT; + } + + /** + * Reads needed data (e.g. index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + this.wide = wide; + if (wide) { + super.setLength(6); + super.setIndexOnly(bytes.readUnsignedShort()); + c = bytes.readShort(); + } else { + super.setLength(3); + super.setIndexOnly(bytes.readUnsignedByte()); + c = bytes.readByte(); + } + } + + /** + * Sets increment factor. + */ + public final void setIncrement(final int c) { + this.c = c; + setWide(); + } + + /** + * Sets index of local variable. + */ + @Override + public final void setIndex(final int n) { + if (n < 0) { + throw new ClassGenException("Negative index value: " + n); + } + super.setIndexOnly(n); + setWide(); + } + + private void setWide() { + wide = super.getIndex() > Const.MAX_BYTE; + if (c > 0) { + wide = wide || c > Byte.MAX_VALUE; + } else { + wide = wide || c < Byte.MIN_VALUE; + } + if (wide) { + super.setLength(6); // wide byte included + } else { + super.setLength(3); + } + } + + /** + * Returns mnemonic for instruction. + * + * @return mnemonic for instruction. + */ + @Override + public String toString(final boolean verbose) { + return super.toString(verbose) + " " + c; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ILOAD.java b/src/main/java/haidnor/jvm/bcel/generic/ILOAD.java new file mode 100644 index 0000000..c11b111 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ILOAD.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * ILOAD - Load int from local variable onto stack + * + *
+ * Stack: ... -> ..., result
+ * 
+ */ +public class ILOAD extends LoadInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + ILOAD() { + super(Const.ILOAD, Const.ILOAD_0); + } + + /** + * Load int from local variable + * + * @param n index of local variable + */ + public ILOAD(final int n) { + super(Const.ILOAD, Const.ILOAD_0, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + super.accept(v); + v.visitILOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IMPDEP1.java b/src/main/java/haidnor/jvm/bcel/generic/IMPDEP1.java new file mode 100644 index 0000000..3f6c0f6 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IMPDEP1.java @@ -0,0 +1,40 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IMPDEP1 - Implementation dependent + */ +public class IMPDEP1 extends Instruction { + + public IMPDEP1() { + super(Const.IMPDEP1, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitIMPDEP1(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IMPDEP2.java b/src/main/java/haidnor/jvm/bcel/generic/IMPDEP2.java new file mode 100644 index 0000000..da7af55 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IMPDEP2.java @@ -0,0 +1,40 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IMPDEP2 - Implementation dependent + */ +public class IMPDEP2 extends Instruction { + + public IMPDEP2() { + super(Const.IMPDEP2, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitIMPDEP2(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IMUL.java b/src/main/java/haidnor/jvm/bcel/generic/IMUL.java new file mode 100644 index 0000000..420fd1a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IMUL.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IMUL - Multiply ints + * + *
+ * Stack: ..., value1, value2 -> result
+ * 
+ */ +public class IMUL extends ArithmeticInstruction { + + /** + * Multiply ints + */ + public IMUL() { + super(Const.IMUL); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitIMUL(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/INEG.java b/src/main/java/haidnor/jvm/bcel/generic/INEG.java new file mode 100644 index 0000000..f83e847 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/INEG.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * INEG - Negate int + * + *
+ * Stack: ..., value -> ..., result
+ * 
+ */ +public class INEG extends ArithmeticInstruction { + + public INEG() { + super(Const.INEG); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitINEG(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/INSTANCEOF.java b/src/main/java/haidnor/jvm/bcel/generic/INSTANCEOF.java new file mode 100644 index 0000000..eafbbd2 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/INSTANCEOF.java @@ -0,0 +1,71 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * INSTANCEOF - Determine if object is of given type + * + *
+ * Stack: ..., objectref -> ..., result
+ * 
+ */ +public class INSTANCEOF extends CPInstruction implements LoadClass, ExceptionThrower, StackProducer, StackConsumer { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + INSTANCEOF() { + } + + public INSTANCEOF(final int index) { + super(Const.INSTANCEOF, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitLoadClass(this); + v.visitExceptionThrower(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitTypedInstruction(this); + v.visitCPInstruction(this); + v.visitINSTANCEOF(this); + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_CLASS_AND_INTERFACE_RESOLUTION); + } + + @Override + public ObjectType getLoadClassType(final ConstantPoolGen cpg) { + Type t = getType(cpg); + if (t instanceof ArrayType) { + t = ((ArrayType) t).getBasicType(); + } + return t instanceof ObjectType ? (ObjectType) t : null; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/INVOKEDYNAMIC.java b/src/main/java/haidnor/jvm/bcel/generic/INVOKEDYNAMIC.java new file mode 100644 index 0000000..133657e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/INVOKEDYNAMIC.java @@ -0,0 +1,124 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; +import haidnor.jvm.bcel.classfile.ConstantInvokeDynamic; +import haidnor.jvm.bcel.classfile.ConstantNameAndType; +import haidnor.jvm.bcel.classfile.ConstantPool; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Class for INVOKEDYNAMIC. Not an instance of InvokeInstruction, since that class expects to be able to get the class + * of the method. Ignores the bootstrap mechanism entirely. + * + * @see The + * invokedynamic instruction in The Java Virtual Machine Specification + * @since 6.0 + */ +public class INVOKEDYNAMIC extends InvokeInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + INVOKEDYNAMIC() { + } + + public INVOKEDYNAMIC(final int index) { + super(Const.INVOKEDYNAMIC, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackConsumer(this); + v.visitStackProducer(this); + v.visitLoadClass(this); + v.visitCPInstruction(this); + v.visitFieldOrMethod(this); + v.visitInvokeInstruction(this); + v.visitINVOKEDYNAMIC(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(super.getOpcode()); + out.writeShort(super.getIndex()); + out.writeByte(0); + out.writeByte(0); + } + + /** + * Override the parent method because our class name is held elsewhere. + * + * Note: Contrary to this method's name it does not return the class name of the invoke target; rather it returns the + * name of the method that will be used to invoke the Lambda method generated by this invoke dynamic instruction. + */ + @Override + public String getClassName(final ConstantPoolGen cpg) { + final ConstantPool cp = cpg.getConstantPool(); + final ConstantInvokeDynamic cid = cp.getConstant(super.getIndex(), Const.CONSTANT_InvokeDynamic, ConstantInvokeDynamic.class); + return cp.getConstant(cid.getNameAndTypeIndex(), ConstantNameAndType.class).getName(cp); + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_INTERFACE_METHOD_RESOLUTION, ExceptionConst.UNSATISFIED_LINK_ERROR, + ExceptionConst.ABSTRACT_METHOD_ERROR, ExceptionConst.ILLEGAL_ACCESS_ERROR, ExceptionConst.INCOMPATIBLE_CLASS_CHANGE_ERROR); + } + + /** + * Since InvokeDynamic doesn't refer to a reference type, just return java.lang.Object, as that is the only type we can + * say for sure the reference will be. + * + * @param cpg the ConstantPoolGen used to create the instruction + * @return an ObjectType for java.lang.Object + * @since 6.1 + */ + @Override + public ReferenceType getReferenceType(final ConstantPoolGen cpg) { + return new ObjectType(Object.class.getName()); + } + + /** + * Read needed data (i.e., index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + super.initFromFile(bytes, wide); + super.setLength(5); + bytes.readByte(); // Skip 0 byte + bytes.readByte(); // Skip 0 byte + } + +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/INVOKEINTERFACE.java b/src/main/java/haidnor/jvm/bcel/generic/INVOKEINTERFACE.java new file mode 100644 index 0000000..8cf9d47 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/INVOKEINTERFACE.java @@ -0,0 +1,124 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; +import haidnor.jvm.bcel.classfile.ConstantPool; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * INVOKEINTERFACE - Invoke interface method + * + *
+ * Stack: ..., objectref, [arg1, [arg2 ...]] -> ...
+ * 
+ * + * @see The + * invokeinterface instruction in The Java Virtual Machine Specification + */ +public final class INVOKEINTERFACE extends InvokeInstruction { + + private int nargs; // Number of arguments on stack (number of stack slots), called "count" in vmspec2 + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + INVOKEINTERFACE() { + } + + public INVOKEINTERFACE(final int index, final int nargs) { + super(Const.INVOKEINTERFACE, index); + super.setLength(5); + if (nargs < 1) { + throw new ClassGenException("Number of arguments must be > 0 " + nargs); + } + this.nargs = nargs; + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackConsumer(this); + v.visitStackProducer(this); + v.visitLoadClass(this); + v.visitCPInstruction(this); + v.visitFieldOrMethod(this); + v.visitInvokeInstruction(this); + v.visitINVOKEINTERFACE(this); + } + + @Override + public int consumeStack(final ConstantPoolGen cpg) { // nargs is given in byte-code + return nargs; // nargs includes this reference + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(super.getOpcode()); + out.writeShort(super.getIndex()); + out.writeByte(nargs); + out.writeByte(0); + } + + /** + * The count argument according to the Java Language Specification, Second Edition. + */ + public int getCount() { + return nargs; + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_INTERFACE_METHOD_RESOLUTION, ExceptionConst.UNSATISFIED_LINK_ERROR, + ExceptionConst.ABSTRACT_METHOD_ERROR, ExceptionConst.ILLEGAL_ACCESS_ERROR, ExceptionConst.INCOMPATIBLE_CLASS_CHANGE_ERROR); + } + + /** + * Read needed data (i.e., index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + super.initFromFile(bytes, wide); + super.setLength(5); + nargs = bytes.readUnsignedByte(); + bytes.readByte(); // Skip 0 byte + } + + /** + * @return mnemonic for instruction with symbolic references resolved + */ + @Override + public String toString(final ConstantPool cp) { + return super.toString(cp) + " " + nargs; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/INVOKESPECIAL.java b/src/main/java/haidnor/jvm/bcel/generic/INVOKESPECIAL.java new file mode 100644 index 0000000..1ab8998 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/INVOKESPECIAL.java @@ -0,0 +1,83 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * INVOKESPECIAL - Invoke instance method; special handling for superclass, private and instance initialization method + * invocations + * + *
+ * Stack: ..., objectref, [arg1, [arg2 ...]] -> ...
+ * 
+ * + * @see The + * invokespecial instruction in The Java Virtual Machine Specification + */ +public class INVOKESPECIAL extends InvokeInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + INVOKESPECIAL() { + } + + public INVOKESPECIAL(final int index) { + super(Const.INVOKESPECIAL, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackConsumer(this); + v.visitStackProducer(this); + v.visitLoadClass(this); + v.visitCPInstruction(this); + v.visitFieldOrMethod(this); + v.visitInvokeInstruction(this); + v.visitINVOKESPECIAL(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(super.getOpcode()); + out.writeShort(super.getIndex()); + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_FIELD_AND_METHOD_RESOLUTION, ExceptionConst.NULL_POINTER_EXCEPTION, + ExceptionConst.INCOMPATIBLE_CLASS_CHANGE_ERROR, ExceptionConst.ABSTRACT_METHOD_ERROR, ExceptionConst.UNSATISFIED_LINK_ERROR); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/INVOKESTATIC.java b/src/main/java/haidnor/jvm/bcel/generic/INVOKESTATIC.java new file mode 100644 index 0000000..1890895 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/INVOKESTATIC.java @@ -0,0 +1,82 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * INVOKESTATIC - Invoke a class (static) method + * + *
+ * Stack: ..., [arg1, [arg2 ...]] -> ...
+ * 
+ * + * @see The invokestatic + * instruction in The Java Virtual Machine Specification + */ +public class INVOKESTATIC extends InvokeInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + INVOKESTATIC() { + } + + public INVOKESTATIC(final int index) { + super(Const.INVOKESTATIC, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackConsumer(this); + v.visitStackProducer(this); + v.visitLoadClass(this); + v.visitCPInstruction(this); + v.visitFieldOrMethod(this); + v.visitInvokeInstruction(this); + v.visitINVOKESTATIC(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(super.getOpcode()); + out.writeShort(super.getIndex()); + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_FIELD_AND_METHOD_RESOLUTION, ExceptionConst.UNSATISFIED_LINK_ERROR, + ExceptionConst.INCOMPATIBLE_CLASS_CHANGE_ERROR); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/INVOKEVIRTUAL.java b/src/main/java/haidnor/jvm/bcel/generic/INVOKEVIRTUAL.java new file mode 100644 index 0000000..ddbd29d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/INVOKEVIRTUAL.java @@ -0,0 +1,82 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * INVOKEVIRTUAL - Invoke instance method; dispatch based on class + * + *
+ * Stack: ..., objectref, [arg1, [arg2 ...]] -> ...
+ * 
+ * + * @see The + * invokevirtual instruction in The Java Virtual Machine Specification + */ +public class INVOKEVIRTUAL extends InvokeInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + INVOKEVIRTUAL() { + } + + public INVOKEVIRTUAL(final int index) { + super(Const.INVOKEVIRTUAL, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackConsumer(this); + v.visitStackProducer(this); + v.visitLoadClass(this); + v.visitCPInstruction(this); + v.visitFieldOrMethod(this); + v.visitInvokeInstruction(this); + v.visitINVOKEVIRTUAL(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(super.getOpcode()); + out.writeShort(super.getIndex()); + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_FIELD_AND_METHOD_RESOLUTION, ExceptionConst.NULL_POINTER_EXCEPTION, + ExceptionConst.INCOMPATIBLE_CLASS_CHANGE_ERROR, ExceptionConst.ABSTRACT_METHOD_ERROR, ExceptionConst.UNSATISFIED_LINK_ERROR); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IOR.java b/src/main/java/haidnor/jvm/bcel/generic/IOR.java new file mode 100644 index 0000000..6734e87 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IOR.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IOR - Bitwise OR int + * + *
+ * Stack: ..., value1, value2 -> ..., result
+ * 
+ */ +public class IOR extends ArithmeticInstruction { + + public IOR() { + super(Const.IOR); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitIOR(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IREM.java b/src/main/java/haidnor/jvm/bcel/generic/IREM.java new file mode 100644 index 0000000..c1262bc --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IREM.java @@ -0,0 +1,61 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * IREM - Remainder of int + * + *
+ * Stack: ..., value1, value2 -> result
+ * 
+ */ +public class IREM extends ArithmeticInstruction implements ExceptionThrower { + + /** + * Remainder of ints + */ + public IREM() { + super(Const.IREM); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitIREM(this); + } + + /** + * @return exceptions this instruction may cause + */ + @Override + public Class[] getExceptions() { + return new Class[] {ExceptionConst.ARITHMETIC_EXCEPTION}; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IRETURN.java b/src/main/java/haidnor/jvm/bcel/generic/IRETURN.java new file mode 100644 index 0000000..acc0ae6 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IRETURN.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IRETURN - Return int from method + * + *
+ * Stack: ..., value -> <empty>
+ * 
+ */ +public class IRETURN extends ReturnInstruction { + + /** + * Return int from method + */ + public IRETURN() { + super(Const.IRETURN); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackConsumer(this); + v.visitReturnInstruction(this); + v.visitIRETURN(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ISHL.java b/src/main/java/haidnor/jvm/bcel/generic/ISHL.java new file mode 100644 index 0000000..5875715 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ISHL.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * ISHL - Arithmetic shift left int + * + *
+ * Stack: ..., value1, value2 -> ..., result
+ * 
+ */ +public class ISHL extends ArithmeticInstruction { + + public ISHL() { + super(Const.ISHL); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitISHL(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ISHR.java b/src/main/java/haidnor/jvm/bcel/generic/ISHR.java new file mode 100644 index 0000000..fee23e7 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ISHR.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * ISHR - Arithmetic shift right int + * + *
+ * Stack: ..., value1, value2 -> ..., result
+ * 
+ */ +public class ISHR extends ArithmeticInstruction { + + public ISHR() { + super(Const.ISHR); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitISHR(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ISTORE.java b/src/main/java/haidnor/jvm/bcel/generic/ISTORE.java new file mode 100644 index 0000000..a0f38d9 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ISTORE.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * ISTORE - Store int from stack into local variable + * + *
+ * Stack: ..., value -> ...
+ * 
+ */ +public class ISTORE extends StoreInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + ISTORE() { + super(Const.ISTORE, Const.ISTORE_0); + } + + /** + * Store int into local variable + * + * @param n index of local variable + */ + public ISTORE(final int n) { + super(Const.ISTORE, Const.ISTORE_0, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + super.accept(v); + v.visitISTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ISUB.java b/src/main/java/haidnor/jvm/bcel/generic/ISUB.java new file mode 100644 index 0000000..4708028 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ISUB.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * ISUB - Substract ints + * + *
+ * Stack: ..., value1, value2 -> result
+ * 
+ */ +public class ISUB extends ArithmeticInstruction { + + /** + * Substract ints + */ + public ISUB() { + super(Const.ISUB); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitISUB(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IUSHR.java b/src/main/java/haidnor/jvm/bcel/generic/IUSHR.java new file mode 100644 index 0000000..ac887f0 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IUSHR.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IUSHR - Logical shift right int + * + *
+ * Stack: ..., value1, value2 -> ..., result
+ * 
+ */ +public class IUSHR extends ArithmeticInstruction { + + public IUSHR() { + super(Const.IUSHR); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitIUSHR(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IXOR.java b/src/main/java/haidnor/jvm/bcel/generic/IXOR.java new file mode 100644 index 0000000..6cd1001 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IXOR.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * IXOR - Bitwise XOR int + * + *
+ * Stack: ..., value1, value2 -> ..., result
+ * 
+ */ +public class IXOR extends ArithmeticInstruction { + + public IXOR() { + super(Const.IXOR); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitIXOR(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IfInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/IfInstruction.java new file mode 100644 index 0000000..4db56c7 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IfInstruction.java @@ -0,0 +1,42 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Super class for the IFxxx family of instructions. + */ +public abstract class IfInstruction extends BranchInstruction implements StackConsumer { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + IfInstruction() { + } + + /** + * @param opcode opcode of instruction + * @param target Target instruction to branch to + */ + protected IfInstruction(final short opcode, final InstructionHandle target) { + super(opcode, target); + } + + /** + * @return negation of instruction, e.g. IFEQ.negate() == IFNE + */ + public abstract IfInstruction negate(); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/IndexedInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/IndexedInstruction.java new file mode 100644 index 0000000..5429d02 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/IndexedInstruction.java @@ -0,0 +1,27 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denote entity that refers to an index, e.g. local variable instructions, RET, CPInstruction, etc. + */ +public interface IndexedInstruction { + + int getIndex(); + + void setIndex(int index); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/Instruction.java b/src/main/java/haidnor/jvm/bcel/generic/Instruction.java new file mode 100644 index 0000000..92cc653 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/Instruction.java @@ -0,0 +1,596 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.ConstantPool; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Abstract super class for all Java byte codes. + */ +public abstract class Instruction implements Cloneable { + + static final Instruction[] EMPTY_ARRAY = {}; + + private static InstructionComparator cmp = InstructionComparator.DEFAULT; + + /** + * Gets Comparator object used in the equals() method to determine equality of instructions. + * + * @return currently used comparator for equals() + * @deprecated (6.0) use the built in comparator, or wrap this class in another object that implements these methods + */ + @Deprecated + public static InstructionComparator getComparator() { + return cmp; + } + + /** + * Tests if the value can fit in a byte (signed) + * + * @param value the value to check + * @return true if the value is in range + * @since 6.0 + */ + public static boolean isValidByte(final int value) { + return value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE; + } + + /** + * Tests if the value can fit in a short (signed) + * + * @param value the value to check + * @return true if the value is in range + * @since 6.0 + */ + public static boolean isValidShort(final int value) { + return value >= Short.MIN_VALUE && value <= Short.MAX_VALUE; + } + + /** + * Reads an instruction from (byte code) input stream and return the appropriate object. + *

+ * If the Instruction is defined in {@link InstructionConst}, then the singleton instance is returned. + *

+ * @param bytes input stream bytes + * @return instruction object being read + * @throws IOException Thrown when an I/O exception of some sort has occurred. + * @see InstructionConst#getInstruction(int) + */ + // @since 6.0 no longer final + public static Instruction readInstruction(final ByteSequence bytes) throws IOException { + boolean wide = false; + short opcode = (short) bytes.readUnsignedByte(); + Instruction obj = null; + if (opcode == Const.WIDE) { // Read next opcode after wide byte + wide = true; + opcode = (short) bytes.readUnsignedByte(); + } + final Instruction instruction = InstructionConst.getInstruction(opcode); + if (instruction != null) { + return instruction; // Used predefined immutable object, if available + } + + switch (opcode) { + case Const.BIPUSH: + obj = new BIPUSH(); + break; + case Const.SIPUSH: + obj = new SIPUSH(); + break; + case Const.LDC: + obj = new LDC(); + break; + case Const.LDC_W: + obj = new LDC_W(); + break; + case Const.LDC2_W: + obj = new LDC2_W(); + break; + case Const.ILOAD: + obj = new ILOAD(); + break; + case Const.LLOAD: + obj = new LLOAD(); + break; + case Const.FLOAD: + obj = new FLOAD(); + break; + case Const.DLOAD: + obj = new DLOAD(); + break; + case Const.ALOAD: + obj = new ALOAD(); + break; + case Const.ILOAD_0: + obj = new ILOAD(0); + break; + case Const.ILOAD_1: + obj = new ILOAD(1); + break; + case Const.ILOAD_2: + obj = new ILOAD(2); + break; + case Const.ILOAD_3: + obj = new ILOAD(3); + break; + case Const.LLOAD_0: + obj = new LLOAD(0); + break; + case Const.LLOAD_1: + obj = new LLOAD(1); + break; + case Const.LLOAD_2: + obj = new LLOAD(2); + break; + case Const.LLOAD_3: + obj = new LLOAD(3); + break; + case Const.FLOAD_0: + obj = new FLOAD(0); + break; + case Const.FLOAD_1: + obj = new FLOAD(1); + break; + case Const.FLOAD_2: + obj = new FLOAD(2); + break; + case Const.FLOAD_3: + obj = new FLOAD(3); + break; + case Const.DLOAD_0: + obj = new DLOAD(0); + break; + case Const.DLOAD_1: + obj = new DLOAD(1); + break; + case Const.DLOAD_2: + obj = new DLOAD(2); + break; + case Const.DLOAD_3: + obj = new DLOAD(3); + break; + case Const.ALOAD_0: + obj = new ALOAD(0); + break; + case Const.ALOAD_1: + obj = new ALOAD(1); + break; + case Const.ALOAD_2: + obj = new ALOAD(2); + break; + case Const.ALOAD_3: + obj = new ALOAD(3); + break; + case Const.ISTORE: + obj = new ISTORE(); + break; + case Const.LSTORE: + obj = new LSTORE(); + break; + case Const.FSTORE: + obj = new FSTORE(); + break; + case Const.DSTORE: + obj = new DSTORE(); + break; + case Const.ASTORE: + obj = new ASTORE(); + break; + case Const.ISTORE_0: + obj = new ISTORE(0); + break; + case Const.ISTORE_1: + obj = new ISTORE(1); + break; + case Const.ISTORE_2: + obj = new ISTORE(2); + break; + case Const.ISTORE_3: + obj = new ISTORE(3); + break; + case Const.LSTORE_0: + obj = new LSTORE(0); + break; + case Const.LSTORE_1: + obj = new LSTORE(1); + break; + case Const.LSTORE_2: + obj = new LSTORE(2); + break; + case Const.LSTORE_3: + obj = new LSTORE(3); + break; + case Const.FSTORE_0: + obj = new FSTORE(0); + break; + case Const.FSTORE_1: + obj = new FSTORE(1); + break; + case Const.FSTORE_2: + obj = new FSTORE(2); + break; + case Const.FSTORE_3: + obj = new FSTORE(3); + break; + case Const.DSTORE_0: + obj = new DSTORE(0); + break; + case Const.DSTORE_1: + obj = new DSTORE(1); + break; + case Const.DSTORE_2: + obj = new DSTORE(2); + break; + case Const.DSTORE_3: + obj = new DSTORE(3); + break; + case Const.ASTORE_0: + obj = new ASTORE(0); + break; + case Const.ASTORE_1: + obj = new ASTORE(1); + break; + case Const.ASTORE_2: + obj = new ASTORE(2); + break; + case Const.ASTORE_3: + obj = new ASTORE(3); + break; + case Const.IINC: + obj = new IINC(); + break; + case Const.IFEQ: + obj = new IFEQ(); + break; + case Const.IFNE: + obj = new IFNE(); + break; + case Const.IFLT: + obj = new IFLT(); + break; + case Const.IFGE: + obj = new IFGE(); + break; + case Const.IFGT: + obj = new IFGT(); + break; + case Const.IFLE: + obj = new IFLE(); + break; + case Const.IF_ICMPEQ: + obj = new IF_ICMPEQ(); + break; + case Const.IF_ICMPNE: + obj = new IF_ICMPNE(); + break; + case Const.IF_ICMPLT: + obj = new IF_ICMPLT(); + break; + case Const.IF_ICMPGE: + obj = new IF_ICMPGE(); + break; + case Const.IF_ICMPGT: + obj = new IF_ICMPGT(); + break; + case Const.IF_ICMPLE: + obj = new IF_ICMPLE(); + break; + case Const.IF_ACMPEQ: + obj = new IF_ACMPEQ(); + break; + case Const.IF_ACMPNE: + obj = new IF_ACMPNE(); + break; + case Const.GOTO: + obj = new GOTO(); + break; + case Const.JSR: + obj = new JSR(); + break; + case Const.RET: + obj = new RET(); + break; + case Const.TABLESWITCH: + obj = new TABLESWITCH(); + break; + case Const.LOOKUPSWITCH: + obj = new LOOKUPSWITCH(); + break; + case Const.GETSTATIC: + obj = new GETSTATIC(); + break; + case Const.PUTSTATIC: + obj = new PUTSTATIC(); + break; + case Const.GETFIELD: + obj = new GETFIELD(); + break; + case Const.PUTFIELD: + obj = new PUTFIELD(); + break; + case Const.INVOKEVIRTUAL: + obj = new INVOKEVIRTUAL(); + break; + case Const.INVOKESPECIAL: + obj = new INVOKESPECIAL(); + break; + case Const.INVOKESTATIC: + obj = new INVOKESTATIC(); + break; + case Const.INVOKEINTERFACE: + obj = new INVOKEINTERFACE(); + break; + case Const.INVOKEDYNAMIC: + obj = new INVOKEDYNAMIC(); + break; + case Const.NEW: + obj = new NEW(); + break; + case Const.NEWARRAY: + obj = new NEWARRAY(); + break; + case Const.ANEWARRAY: + obj = new ANEWARRAY(); + break; + case Const.CHECKCAST: + obj = new CHECKCAST(); + break; + case Const.INSTANCEOF: + obj = new INSTANCEOF(); + break; + case Const.MULTIANEWARRAY: + obj = new MULTIANEWARRAY(); + break; + case Const.IFNULL: + obj = new IFNULL(); + break; + case Const.IFNONNULL: + obj = new IFNONNULL(); + break; + case Const.GOTO_W: + obj = new GOTO_W(); + break; + case Const.JSR_W: + obj = new JSR_W(); + break; + case Const.BREAKPOINT: + obj = new BREAKPOINT(); + break; + case Const.IMPDEP1: + obj = new IMPDEP1(); + break; + case Const.IMPDEP2: + obj = new IMPDEP2(); + break; + default: + throw new ClassGenException("Illegal opcode detected: " + opcode); + + } + + if (wide && !(obj instanceof LocalVariableInstruction || obj instanceof RET)) { + throw new ClassGenException("Illegal opcode after wide: " + opcode); + } + obj.setOpcode(opcode); + obj.initFromFile(bytes, wide); // Do further initializations, if any + return obj; + } + + /** + * Sets comparator to be used for equals(). + * + * @deprecated (6.0) use the built in comparator, or wrap this class in another object that implements these methods + */ + @Deprecated + public static void setComparator(final InstructionComparator c) { + cmp = c; + } + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected short length = 1; // Length of instruction in bytes + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected short opcode = -1; // Opcode number + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + Instruction() { + } + + public Instruction(final short opcode, final short length) { + this.length = length; + this.opcode = opcode; + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + public abstract void accept(Visitor v); + + /** + * This method also gives right results for instructions whose effect on the stack depends on the constant pool entry + * they reference. + * + * @return Number of words consumed from stack by this instruction, or Constants.UNPREDICTABLE, if this can not be + * computed statically + */ + public int consumeStack(final ConstantPoolGen cpg) { + return Const.getConsumeStack(opcode); + } + + /** + * Use with caution, since 'BranchInstruction's have a 'target' reference which is not copied correctly (only basic + * types are). This also applies for 'Select' instructions with their multiple branch targets. + * + * @see BranchInstruction + * @return (shallow) copy of an instruction + */ + public Instruction copy() { + Instruction i = null; + // "Constant" instruction, no need to duplicate + if (InstructionConst.getInstruction(this.getOpcode()) != null) { + i = this; + } else { + try { + i = (Instruction) clone(); + } catch (final CloneNotSupportedException e) { + System.err.println(e); + } + } + return i; + } + + /** + * Some instructions may be reused, so don't do anything by default. + */ + void dispose() { + } + + /** + * Dumps instruction as byte code to stream out. + * + * @param out Output stream + * @throws IOException Thrown when an I/O exception of some sort has occurred. + */ + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(opcode); // Common for all instructions + } + + /** + * Tests for equality, delegated to comparator + * + * @return true if that is an Instruction and has the same opcode + */ + @Override + public boolean equals(final Object that) { + return that instanceof Instruction && cmp.equals(this, (Instruction) that); + } + + /** + * @return length (in bytes) of instruction + */ + public int getLength() { + return length; + } + + /** + * @return name of instruction, i.e., opcode name + */ + public String getName() { + return Const.getOpcodeName(opcode); + } + + /** + * @return this instructions opcode + */ + public short getOpcode() { + return opcode; + } + + /** + * Gets the hashCode of this object. + * + * @return the hashCode + * @since 6.0 + */ + @Override + public int hashCode() { + return opcode; + } + + /** + * Reads needed data (e.g. index) from file. + * + * @param bytes byte sequence to read from + * @param wide "wide" instruction flag + * @throws IOException may be thrown if the implementation needs to read data from the file + */ + @SuppressWarnings("unused") // thrown by subclasses + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + } + + /** + * This method also gives right results for instructions whose effect on the stack depends on the constant pool entry + * they reference. + * + * @return Number of words produced onto stack by this instruction, or Constants.UNPREDICTABLE, if this can not be + * computed statically + */ + public int produceStack(final ConstantPoolGen cpg) { + return Const.getProduceStack(opcode); + } + + /** + * Needed in readInstruction and subclasses in this package + * + * @since 6.0 + */ + final void setLength(final int length) { + this.length = (short) length; // TODO check range? + } + + /** + * Needed in readInstruction and subclasses in this package + */ + final void setOpcode(final short opcode) { + this.opcode = opcode; + } + + /** + * @return mnemonic for instruction in verbose format + */ + @Override + public String toString() { + return toString(true); + } + + /** + * Long output format: + * + * <name of opcode> "["<opcode number>"]" "("<length of instruction>")" + * + * @param verbose long/short format switch + * @return mnemonic for instruction + */ + public String toString(final boolean verbose) { + if (verbose) { + return getName() + "[" + opcode + "](" + length + ")"; + } + return getName(); + } + + /** + * @return mnemonic for instruction with sumbolic references resolved + */ + public String toString(final ConstantPool cp) { + return toString(false); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/InstructionComparator.java b/src/main/java/haidnor/jvm/bcel/generic/InstructionComparator.java new file mode 100644 index 0000000..400dfcc --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/InstructionComparator.java @@ -0,0 +1,55 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Equality of instructions isn't clearly to be defined. You might wish, for example, to compare whether instructions + * have the same meaning. E.g., whether two INVOKEVIRTUALs describe the same call. + *

+ * The DEFAULT comparator however, considers two instructions to be equal if they have same opcode and point to the same + * indexes (if any) in the constant pool or the same local variable index. Branch instructions must have the same + * target. + *

+ * + * @see Instruction + */ +public interface InstructionComparator { + + InstructionComparator DEFAULT = (i1, i2) -> { + if (i1.getOpcode() == i2.getOpcode()) { + if (i1 instanceof BranchInstruction) { + // BIs are never equal to make targeters work correctly (BCEL-195) + return false; +// } else if (i1 == i2) { TODO consider adding this shortcut +// return true; // this must be AFTER the BI test + } + if (i1 instanceof ConstantPushInstruction) { + return ((ConstantPushInstruction) i1).getValue().equals(((ConstantPushInstruction) i2).getValue()); + } + if (i1 instanceof IndexedInstruction) { + return ((IndexedInstruction) i1).getIndex() == ((IndexedInstruction) i2).getIndex(); + } + if (i1 instanceof NEWARRAY) { + return ((NEWARRAY) i1).getTypecode() == ((NEWARRAY) i2).getTypecode(); + } + return true; + } + return false; + }; + + boolean equals(Instruction i1, Instruction i2); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/InstructionConst.java b/src/main/java/haidnor/jvm/bcel/generic/InstructionConst.java new file mode 100644 index 0000000..3e3834a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/InstructionConst.java @@ -0,0 +1,294 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * Contains shareable instruction objects. + *

+ * In order to save memory you can use some instructions multiply, since they have an immutable state and are directly + * derived from Instruction. I.e. they have no instance fields that could be changed. Since some of these instructions + * like ICONST_0 occur very frequently this can save a lot of time and space. This feature is an adaptation of the + * FlyWeight design pattern, we just use an array instead of a factory. + *

+ *

+ * The Instructions can also accessed directly under their names, so it's possible to write + * il.append(Instruction.ICONST_0); + *

+ */ +public final class InstructionConst { + + /** + * Predefined instruction objects. + * + * NOTE these are not currently immutable, because Instruction has mutable protected fields opcode and length. + */ + public static final Instruction NOP = new NOP(); + public static final Instruction ACONST_NULL = new ACONST_NULL(); + public static final Instruction ICONST_M1 = new ICONST(-1); + public static final Instruction ICONST_0 = new ICONST(0); + public static final Instruction ICONST_1 = new ICONST(1); + public static final Instruction ICONST_2 = new ICONST(2); + public static final Instruction ICONST_3 = new ICONST(3); + public static final Instruction ICONST_4 = new ICONST(4); + public static final Instruction ICONST_5 = new ICONST(5); + public static final Instruction LCONST_0 = new LCONST(0); + public static final Instruction LCONST_1 = new LCONST(1); + public static final Instruction FCONST_0 = new FCONST(0); + public static final Instruction FCONST_1 = new FCONST(1); + public static final Instruction FCONST_2 = new FCONST(2); + public static final Instruction DCONST_0 = new DCONST(0); + public static final Instruction DCONST_1 = new DCONST(1); + public static final ArrayInstruction IALOAD = new IALOAD(); + public static final ArrayInstruction LALOAD = new LALOAD(); + public static final ArrayInstruction FALOAD = new FALOAD(); + public static final ArrayInstruction DALOAD = new DALOAD(); + public static final ArrayInstruction AALOAD = new AALOAD(); + public static final ArrayInstruction BALOAD = new BALOAD(); + public static final ArrayInstruction CALOAD = new CALOAD(); + public static final ArrayInstruction SALOAD = new SALOAD(); + public static final ArrayInstruction IASTORE = new IASTORE(); + public static final ArrayInstruction LASTORE = new LASTORE(); + public static final ArrayInstruction FASTORE = new FASTORE(); + public static final ArrayInstruction DASTORE = new DASTORE(); + public static final ArrayInstruction AASTORE = new AASTORE(); + public static final ArrayInstruction BASTORE = new BASTORE(); + public static final ArrayInstruction CASTORE = new CASTORE(); + public static final ArrayInstruction SASTORE = new SASTORE(); + public static final StackInstruction POP = new POP(); + public static final StackInstruction POP2 = new POP2(); + public static final StackInstruction DUP = new DUP(); + public static final StackInstruction DUP_X1 = new DUP_X1(); + public static final StackInstruction DUP_X2 = new DUP_X2(); + public static final StackInstruction DUP2 = new DUP2(); + public static final StackInstruction DUP2_X1 = new DUP2_X1(); + public static final StackInstruction DUP2_X2 = new DUP2_X2(); + public static final StackInstruction SWAP = new SWAP(); + public static final ArithmeticInstruction IADD = new IADD(); + public static final ArithmeticInstruction LADD = new LADD(); + public static final ArithmeticInstruction FADD = new FADD(); + public static final ArithmeticInstruction DADD = new DADD(); + public static final ArithmeticInstruction ISUB = new ISUB(); + public static final ArithmeticInstruction LSUB = new LSUB(); + public static final ArithmeticInstruction FSUB = new FSUB(); + public static final ArithmeticInstruction DSUB = new DSUB(); + public static final ArithmeticInstruction IMUL = new IMUL(); + public static final ArithmeticInstruction LMUL = new LMUL(); + public static final ArithmeticInstruction FMUL = new FMUL(); + public static final ArithmeticInstruction DMUL = new DMUL(); + public static final ArithmeticInstruction IDIV = new IDIV(); + public static final ArithmeticInstruction LDIV = new LDIV(); + public static final ArithmeticInstruction FDIV = new FDIV(); + public static final ArithmeticInstruction DDIV = new DDIV(); + public static final ArithmeticInstruction IREM = new IREM(); + public static final ArithmeticInstruction LREM = new LREM(); + public static final ArithmeticInstruction FREM = new FREM(); + public static final ArithmeticInstruction DREM = new DREM(); + public static final ArithmeticInstruction INEG = new INEG(); + public static final ArithmeticInstruction LNEG = new LNEG(); + public static final ArithmeticInstruction FNEG = new FNEG(); + public static final ArithmeticInstruction DNEG = new DNEG(); + public static final ArithmeticInstruction ISHL = new ISHL(); + public static final ArithmeticInstruction LSHL = new LSHL(); + public static final ArithmeticInstruction ISHR = new ISHR(); + public static final ArithmeticInstruction LSHR = new LSHR(); + public static final ArithmeticInstruction IUSHR = new IUSHR(); + public static final ArithmeticInstruction LUSHR = new LUSHR(); + public static final ArithmeticInstruction IAND = new IAND(); + public static final ArithmeticInstruction LAND = new LAND(); + public static final ArithmeticInstruction IOR = new IOR(); + public static final ArithmeticInstruction LOR = new LOR(); + public static final ArithmeticInstruction IXOR = new IXOR(); + public static final ArithmeticInstruction LXOR = new LXOR(); + public static final ConversionInstruction I2L = new I2L(); + public static final ConversionInstruction I2F = new I2F(); + public static final ConversionInstruction I2D = new I2D(); + public static final ConversionInstruction L2I = new L2I(); + public static final ConversionInstruction L2F = new L2F(); + public static final ConversionInstruction L2D = new L2D(); + public static final ConversionInstruction F2I = new F2I(); + public static final ConversionInstruction F2L = new F2L(); + public static final ConversionInstruction F2D = new F2D(); + public static final ConversionInstruction D2I = new D2I(); + public static final ConversionInstruction D2L = new D2L(); + public static final ConversionInstruction D2F = new D2F(); + public static final ConversionInstruction I2B = new I2B(); + public static final ConversionInstruction I2C = new I2C(); + public static final ConversionInstruction I2S = new I2S(); + public static final Instruction LCMP = new LCMP(); + public static final Instruction FCMPL = new FCMPL(); + public static final Instruction FCMPG = new FCMPG(); + public static final Instruction DCMPL = new DCMPL(); + public static final Instruction DCMPG = new DCMPG(); + public static final ReturnInstruction IRETURN = new IRETURN(); + public static final ReturnInstruction LRETURN = new LRETURN(); + public static final ReturnInstruction FRETURN = new FRETURN(); + public static final ReturnInstruction DRETURN = new DRETURN(); + public static final ReturnInstruction ARETURN = new ARETURN(); + public static final ReturnInstruction RETURN = new RETURN(); + public static final Instruction ARRAYLENGTH = new ARRAYLENGTH(); + public static final Instruction ATHROW = new ATHROW(); + public static final Instruction MONITORENTER = new MONITORENTER(); + public static final Instruction MONITOREXIT = new MONITOREXIT(); + + /** + * You can use these constants in multiple places safely, if you can guarantee that you will never alter their internal + * values, e.g. call setIndex(). + */ + public static final LocalVariableInstruction THIS = new ALOAD(0); + public static final LocalVariableInstruction ALOAD_0 = THIS; + public static final LocalVariableInstruction ALOAD_1 = new ALOAD(1); + public static final LocalVariableInstruction ALOAD_2 = new ALOAD(2); + public static final LocalVariableInstruction ILOAD_0 = new ILOAD(0); + public static final LocalVariableInstruction ILOAD_1 = new ILOAD(1); + public static final LocalVariableInstruction ILOAD_2 = new ILOAD(2); + public static final LocalVariableInstruction ASTORE_0 = new ASTORE(0); + public static final LocalVariableInstruction ASTORE_1 = new ASTORE(1); + public static final LocalVariableInstruction ASTORE_2 = new ASTORE(2); + public static final LocalVariableInstruction ISTORE_0 = new ISTORE(0); + public static final LocalVariableInstruction ISTORE_1 = new ISTORE(1); + public static final LocalVariableInstruction ISTORE_2 = new ISTORE(2); + + /** + * Get object via its opcode, for immutable instructions like branch instructions entries are set to null. + */ + static final Instruction[] INSTRUCTIONS = new Instruction[256]; + + static { + INSTRUCTIONS[Const.NOP] = NOP; + INSTRUCTIONS[Const.ACONST_NULL] = ACONST_NULL; + INSTRUCTIONS[Const.ICONST_M1] = ICONST_M1; + INSTRUCTIONS[Const.ICONST_0] = ICONST_0; + INSTRUCTIONS[Const.ICONST_1] = ICONST_1; + INSTRUCTIONS[Const.ICONST_2] = ICONST_2; + INSTRUCTIONS[Const.ICONST_3] = ICONST_3; + INSTRUCTIONS[Const.ICONST_4] = ICONST_4; + INSTRUCTIONS[Const.ICONST_5] = ICONST_5; + INSTRUCTIONS[Const.LCONST_0] = LCONST_0; + INSTRUCTIONS[Const.LCONST_1] = LCONST_1; + INSTRUCTIONS[Const.FCONST_0] = FCONST_0; + INSTRUCTIONS[Const.FCONST_1] = FCONST_1; + INSTRUCTIONS[Const.FCONST_2] = FCONST_2; + INSTRUCTIONS[Const.DCONST_0] = DCONST_0; + INSTRUCTIONS[Const.DCONST_1] = DCONST_1; + INSTRUCTIONS[Const.IALOAD] = IALOAD; + INSTRUCTIONS[Const.LALOAD] = LALOAD; + INSTRUCTIONS[Const.FALOAD] = FALOAD; + INSTRUCTIONS[Const.DALOAD] = DALOAD; + INSTRUCTIONS[Const.AALOAD] = AALOAD; + INSTRUCTIONS[Const.BALOAD] = BALOAD; + INSTRUCTIONS[Const.CALOAD] = CALOAD; + INSTRUCTIONS[Const.SALOAD] = SALOAD; + INSTRUCTIONS[Const.IASTORE] = IASTORE; + INSTRUCTIONS[Const.LASTORE] = LASTORE; + INSTRUCTIONS[Const.FASTORE] = FASTORE; + INSTRUCTIONS[Const.DASTORE] = DASTORE; + INSTRUCTIONS[Const.AASTORE] = AASTORE; + INSTRUCTIONS[Const.BASTORE] = BASTORE; + INSTRUCTIONS[Const.CASTORE] = CASTORE; + INSTRUCTIONS[Const.SASTORE] = SASTORE; + INSTRUCTIONS[Const.POP] = POP; + INSTRUCTIONS[Const.POP2] = POP2; + INSTRUCTIONS[Const.DUP] = DUP; + INSTRUCTIONS[Const.DUP_X1] = DUP_X1; + INSTRUCTIONS[Const.DUP_X2] = DUP_X2; + INSTRUCTIONS[Const.DUP2] = DUP2; + INSTRUCTIONS[Const.DUP2_X1] = DUP2_X1; + INSTRUCTIONS[Const.DUP2_X2] = DUP2_X2; + INSTRUCTIONS[Const.SWAP] = SWAP; + INSTRUCTIONS[Const.IADD] = IADD; + INSTRUCTIONS[Const.LADD] = LADD; + INSTRUCTIONS[Const.FADD] = FADD; + INSTRUCTIONS[Const.DADD] = DADD; + INSTRUCTIONS[Const.ISUB] = ISUB; + INSTRUCTIONS[Const.LSUB] = LSUB; + INSTRUCTIONS[Const.FSUB] = FSUB; + INSTRUCTIONS[Const.DSUB] = DSUB; + INSTRUCTIONS[Const.IMUL] = IMUL; + INSTRUCTIONS[Const.LMUL] = LMUL; + INSTRUCTIONS[Const.FMUL] = FMUL; + INSTRUCTIONS[Const.DMUL] = DMUL; + INSTRUCTIONS[Const.IDIV] = IDIV; + INSTRUCTIONS[Const.LDIV] = LDIV; + INSTRUCTIONS[Const.FDIV] = FDIV; + INSTRUCTIONS[Const.DDIV] = DDIV; + INSTRUCTIONS[Const.IREM] = IREM; + INSTRUCTIONS[Const.LREM] = LREM; + INSTRUCTIONS[Const.FREM] = FREM; + INSTRUCTIONS[Const.DREM] = DREM; + INSTRUCTIONS[Const.INEG] = INEG; + INSTRUCTIONS[Const.LNEG] = LNEG; + INSTRUCTIONS[Const.FNEG] = FNEG; + INSTRUCTIONS[Const.DNEG] = DNEG; + INSTRUCTIONS[Const.ISHL] = ISHL; + INSTRUCTIONS[Const.LSHL] = LSHL; + INSTRUCTIONS[Const.ISHR] = ISHR; + INSTRUCTIONS[Const.LSHR] = LSHR; + INSTRUCTIONS[Const.IUSHR] = IUSHR; + INSTRUCTIONS[Const.LUSHR] = LUSHR; + INSTRUCTIONS[Const.IAND] = IAND; + INSTRUCTIONS[Const.LAND] = LAND; + INSTRUCTIONS[Const.IOR] = IOR; + INSTRUCTIONS[Const.LOR] = LOR; + INSTRUCTIONS[Const.IXOR] = IXOR; + INSTRUCTIONS[Const.LXOR] = LXOR; + INSTRUCTIONS[Const.I2L] = I2L; + INSTRUCTIONS[Const.I2F] = I2F; + INSTRUCTIONS[Const.I2D] = I2D; + INSTRUCTIONS[Const.L2I] = L2I; + INSTRUCTIONS[Const.L2F] = L2F; + INSTRUCTIONS[Const.L2D] = L2D; + INSTRUCTIONS[Const.F2I] = F2I; + INSTRUCTIONS[Const.F2L] = F2L; + INSTRUCTIONS[Const.F2D] = F2D; + INSTRUCTIONS[Const.D2I] = D2I; + INSTRUCTIONS[Const.D2L] = D2L; + INSTRUCTIONS[Const.D2F] = D2F; + INSTRUCTIONS[Const.I2B] = I2B; + INSTRUCTIONS[Const.I2C] = I2C; + INSTRUCTIONS[Const.I2S] = I2S; + INSTRUCTIONS[Const.LCMP] = LCMP; + INSTRUCTIONS[Const.FCMPL] = FCMPL; + INSTRUCTIONS[Const.FCMPG] = FCMPG; + INSTRUCTIONS[Const.DCMPL] = DCMPL; + INSTRUCTIONS[Const.DCMPG] = DCMPG; + INSTRUCTIONS[Const.IRETURN] = IRETURN; + INSTRUCTIONS[Const.LRETURN] = LRETURN; + INSTRUCTIONS[Const.FRETURN] = FRETURN; + INSTRUCTIONS[Const.DRETURN] = DRETURN; + INSTRUCTIONS[Const.ARETURN] = ARETURN; + INSTRUCTIONS[Const.RETURN] = RETURN; + INSTRUCTIONS[Const.ARRAYLENGTH] = ARRAYLENGTH; + INSTRUCTIONS[Const.ATHROW] = ATHROW; + INSTRUCTIONS[Const.MONITORENTER] = MONITORENTER; + INSTRUCTIONS[Const.MONITOREXIT] = MONITOREXIT; + } + + /** + * Gets the Instruction. + * + * @param index the index, e.g. {@link Const#RETURN} + * @return the entry from the private INSTRUCTIONS table + */ + public static Instruction getInstruction(final int index) { + return INSTRUCTIONS[index]; + } + + private InstructionConst() { + } // non-instantiable +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/InstructionFactory.java b/src/main/java/haidnor/jvm/bcel/generic/InstructionFactory.java new file mode 100644 index 0000000..bd1e4b6 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/InstructionFactory.java @@ -0,0 +1,734 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * Instances of this class may be used, e.g., to generate typed versions of instructions. Its main purpose is to be used + * as the byte code generating backend of a compiler. You can subclass it to add your own create methods. + *

+ * Note: The static createXXX methods return singleton instances from the {@link InstructionConst} class. + *

+ * + * @see Const + * @see InstructionConst + */ +public class InstructionFactory { + + private static class MethodObject { + + final Type[] argTypes; + final Type resultType; + final String className; + final String name; + + MethodObject(final String c, final String n, final Type r, final Type[] a) { + this.className = c; + this.name = n; + this.resultType = r; + this.argTypes = a; + } + } + + private static final String APPEND = "append"; + + private static final String FQCN_STRING_BUFFER = "java.lang.StringBuffer"; + + // N.N. These must agree with the order of Constants.T_CHAR through T_LONG + private static final String[] shortNames = {"C", "F", "D", "B", "S", "I", "L"}; + + private static final MethodObject[] appendMethodObjects = { + new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[]{Type.STRING}), + new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[]{Type.OBJECT}), null, null, // indices 2, 3 + new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[]{Type.BOOLEAN}), + new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[]{Type.CHAR}), + new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[]{Type.FLOAT}), + new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[]{Type.DOUBLE}), + new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[]{Type.INT}), + new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[]{Type.INT}), // No append(byte) + new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[]{Type.INT}), // No append(short) + new MethodObject(FQCN_STRING_BUFFER, APPEND, Type.STRINGBUFFER, new Type[]{Type.LONG})}; + + /** + * @param type type of elements of array, i.e., array.getElementType() + */ + public static ArrayInstruction createArrayLoad(final Type type) { + switch (type.getType()) { + case Const.T_BOOLEAN: + case Const.T_BYTE: + return InstructionConst.BALOAD; + case Const.T_CHAR: + return InstructionConst.CALOAD; + case Const.T_SHORT: + return InstructionConst.SALOAD; + case Const.T_INT: + return InstructionConst.IALOAD; + case Const.T_FLOAT: + return InstructionConst.FALOAD; + case Const.T_DOUBLE: + return InstructionConst.DALOAD; + case Const.T_LONG: + return InstructionConst.LALOAD; + case Const.T_ARRAY: + case Const.T_OBJECT: + return InstructionConst.AALOAD; + default: + throw new IllegalArgumentException("Invalid type " + type); + } + } + + /** + * @param type type of elements of array, i.e., array.getElementType() + */ + public static ArrayInstruction createArrayStore(final Type type) { + switch (type.getType()) { + case Const.T_BOOLEAN: + case Const.T_BYTE: + return InstructionConst.BASTORE; + case Const.T_CHAR: + return InstructionConst.CASTORE; + case Const.T_SHORT: + return InstructionConst.SASTORE; + case Const.T_INT: + return InstructionConst.IASTORE; + case Const.T_FLOAT: + return InstructionConst.FASTORE; + case Const.T_DOUBLE: + return InstructionConst.DASTORE; + case Const.T_LONG: + return InstructionConst.LASTORE; + case Const.T_ARRAY: + case Const.T_OBJECT: + return InstructionConst.AASTORE; + default: + throw new IllegalArgumentException("Invalid type " + type); + } + } + + private static ArithmeticInstruction createBinaryDoubleOp(final char op) { + switch (op) { + case '-': + return InstructionConst.DSUB; + case '+': + return InstructionConst.DADD; + case '*': + return InstructionConst.DMUL; + case '/': + return InstructionConst.DDIV; + case '%': + return InstructionConst.DREM; + default: + throw new IllegalArgumentException("Invalid operand " + op); + } + } + + private static ArithmeticInstruction createBinaryFloatOp(final char op) { + switch (op) { + case '-': + return InstructionConst.FSUB; + case '+': + return InstructionConst.FADD; + case '*': + return InstructionConst.FMUL; + case '/': + return InstructionConst.FDIV; + case '%': + return InstructionConst.FREM; + default: + throw new IllegalArgumentException("Invalid operand " + op); + } + } + + private static ArithmeticInstruction createBinaryIntOp(final char first, final String op) { + switch (first) { + case '-': + return InstructionConst.ISUB; + case '+': + return InstructionConst.IADD; + case '%': + return InstructionConst.IREM; + case '*': + return InstructionConst.IMUL; + case '/': + return InstructionConst.IDIV; + case '&': + return InstructionConst.IAND; + case '|': + return InstructionConst.IOR; + case '^': + return InstructionConst.IXOR; + case '<': + return InstructionConst.ISHL; + case '>': + return op.equals(">>>") ? InstructionConst.IUSHR : InstructionConst.ISHR; + default: + throw new IllegalArgumentException("Invalid operand " + op); + } + } + + /** + * Create an invokedynamic instruction. + * + * @param bootstrap_index index into the bootstrap_methods array + * @param name name of the called method + * @param ret_type return type of method + * @param argTypes argument types of method + * @see Const + */ + + /* + * createInvokeDynamic only needed if instrumentation code wants to generate a new invokedynamic instruction. I don't + * think we need. + * + * public InvokeInstruction createInvokeDynamic( int bootstrap_index, String name, Type ret_type, Type[] argTypes) { + * int index; int nargs = 0; String signature = Type.getMethodSignature(ret_type, argTypes); for (int i = 0; i < + * argTypes.length; i++) { nargs += argTypes[i].getSize(); } // UNDONE - needs to be added to ConstantPoolGen //index + * = cp.addInvokeDynamic(bootstrap_index, name, signature); index = 0; return new INVOKEDYNAMIC(index); } + */ + private static ArithmeticInstruction createBinaryLongOp(final char first, final String op) { + switch (first) { + case '-': + return InstructionConst.LSUB; + case '+': + return InstructionConst.LADD; + case '%': + return InstructionConst.LREM; + case '*': + return InstructionConst.LMUL; + case '/': + return InstructionConst.LDIV; + case '&': + return InstructionConst.LAND; + case '|': + return InstructionConst.LOR; + case '^': + return InstructionConst.LXOR; + case '<': + return InstructionConst.LSHL; + case '>': + return op.equals(">>>") ? InstructionConst.LUSHR : InstructionConst.LSHR; + default: + throw new IllegalArgumentException("Invalid operand " + op); + } + } + + /** + * Create binary operation for simple basic types, such as int and float. + * + * @param op operation, such as "+", "*", "<<", etc. + */ + public static ArithmeticInstruction createBinaryOperation(final String op, final Type type) { + final char first = op.charAt(0); + switch (type.getType()) { + case Const.T_BYTE: + case Const.T_SHORT: + case Const.T_INT: + case Const.T_CHAR: + return createBinaryIntOp(first, op); + case Const.T_LONG: + return createBinaryLongOp(first, op); + case Const.T_FLOAT: + return createBinaryFloatOp(first); + case Const.T_DOUBLE: + return createBinaryDoubleOp(first); + default: + throw new IllegalArgumentException("Invalid type " + type); + } + } + + /** + * Create branch instruction by given opcode, except LOOKUPSWITCH and TABLESWITCH. For those you should use the SWITCH + * compound instruction. + */ + public static BranchInstruction createBranchInstruction(final short opcode, final InstructionHandle target) { + switch (opcode) { + case Const.IFEQ: + return new IFEQ(target); + case Const.IFNE: + return new IFNE(target); + case Const.IFLT: + return new IFLT(target); + case Const.IFGE: + return new IFGE(target); + case Const.IFGT: + return new IFGT(target); + case Const.IFLE: + return new IFLE(target); + case Const.IF_ICMPEQ: + return new IF_ICMPEQ(target); + case Const.IF_ICMPNE: + return new IF_ICMPNE(target); + case Const.IF_ICMPLT: + return new IF_ICMPLT(target); + case Const.IF_ICMPGE: + return new IF_ICMPGE(target); + case Const.IF_ICMPGT: + return new IF_ICMPGT(target); + case Const.IF_ICMPLE: + return new IF_ICMPLE(target); + case Const.IF_ACMPEQ: + return new IF_ACMPEQ(target); + case Const.IF_ACMPNE: + return new IF_ACMPNE(target); + case Const.GOTO: + return new GOTO(target); + case Const.JSR: + return new JSR(target); + case Const.IFNULL: + return new IFNULL(target); + case Const.IFNONNULL: + return new IFNONNULL(target); + case Const.GOTO_W: + return new GOTO_W(target); + case Const.JSR_W: + return new JSR_W(target); + default: + throw new IllegalArgumentException("Invalid opcode: " + opcode); + } + } + + /** + * @param size size of operand, either 1 (int, e.g.) or 2 (double) + */ + public static StackInstruction createDup(final int size) { + return size == 2 ? InstructionConst.DUP2 : InstructionConst.DUP; + } + + /** + * @param size size of operand, either 1 (int, e.g.) or 2 (double) + */ + public static StackInstruction createDup_1(final int size) { + return size == 2 ? InstructionConst.DUP2_X1 : InstructionConst.DUP_X1; + } + + /** + * @param size size of operand, either 1 (int, e.g.) or 2 (double) + */ + public static StackInstruction createDup_2(final int size) { + return size == 2 ? InstructionConst.DUP2_X2 : InstructionConst.DUP_X2; + } + + /** + * @param index index of local variable + */ + public static LocalVariableInstruction createLoad(final Type type, final int index) { + switch (type.getType()) { + case Const.T_BOOLEAN: + case Const.T_CHAR: + case Const.T_BYTE: + case Const.T_SHORT: + case Const.T_INT: + return new ILOAD(index); + case Const.T_FLOAT: + return new FLOAD(index); + case Const.T_DOUBLE: + return new DLOAD(index); + case Const.T_LONG: + return new LLOAD(index); + case Const.T_ARRAY: + case Const.T_OBJECT: + return new ALOAD(index); + default: + throw new IllegalArgumentException("Invalid type " + type); + } + } + + /** + * Create "null" value for reference types, 0 for basic types like int + */ + public static Instruction createNull(final Type type) { + switch (type.getType()) { + case Const.T_ARRAY: + case Const.T_OBJECT: + return InstructionConst.ACONST_NULL; + case Const.T_INT: + case Const.T_SHORT: + case Const.T_BOOLEAN: + case Const.T_CHAR: + case Const.T_BYTE: + return InstructionConst.ICONST_0; + case Const.T_FLOAT: + return InstructionConst.FCONST_0; + case Const.T_DOUBLE: + return InstructionConst.DCONST_0; + case Const.T_LONG: + return InstructionConst.LCONST_0; + case Const.T_VOID: + return InstructionConst.NOP; + default: + throw new IllegalArgumentException("Invalid type: " + type); + } + } + + /** + * @param size size of operand, either 1 (int, e.g.) or 2 (double) + */ + public static StackInstruction createPop(final int size) { + return size == 2 ? InstructionConst.POP2 : InstructionConst.POP; + } + + /** + * Create typed return + */ + public static ReturnInstruction createReturn(final Type type) { + switch (type.getType()) { + case Const.T_ARRAY: + case Const.T_OBJECT: + return InstructionConst.ARETURN; + case Const.T_INT: + case Const.T_SHORT: + case Const.T_BOOLEAN: + case Const.T_CHAR: + case Const.T_BYTE: + return InstructionConst.IRETURN; + case Const.T_FLOAT: + return InstructionConst.FRETURN; + case Const.T_DOUBLE: + return InstructionConst.DRETURN; + case Const.T_LONG: + return InstructionConst.LRETURN; + case Const.T_VOID: + return InstructionConst.RETURN; + default: + throw new IllegalArgumentException("Invalid type: " + type); + } + } + + /** + * @param index index of local variable + */ + public static LocalVariableInstruction createStore(final Type type, final int index) { + switch (type.getType()) { + case Const.T_BOOLEAN: + case Const.T_CHAR: + case Const.T_BYTE: + case Const.T_SHORT: + case Const.T_INT: + return new ISTORE(index); + case Const.T_FLOAT: + return new FSTORE(index); + case Const.T_DOUBLE: + return new DSTORE(index); + case Const.T_LONG: + return new LSTORE(index); + case Const.T_ARRAY: + case Const.T_OBJECT: + return new ASTORE(index); + default: + throw new IllegalArgumentException("Invalid type " + type); + } + } + + /** + * Create reference to 'this' + */ + public static Instruction createThis() { + return new ALOAD(0); + } + + private static boolean isString(final Type type) { + return type instanceof ObjectType && ((ObjectType) type).getClassName().equals("java.lang.String"); + } + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected ClassGen cg; + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected ConstantPoolGen cp; + + /** + * Initialize with ClassGen object + */ + public InstructionFactory(final ClassGen cg) { + this(cg, cg.getConstantPool()); + } + + public InstructionFactory(final ClassGen cg, final ConstantPoolGen cp) { + this.cg = cg; + this.cp = cp; + } + + /** + * Initialize just with ConstantPoolGen object + */ + public InstructionFactory(final ConstantPoolGen cp) { + this(null, cp); + } + + public Instruction createAppend(final Type type) { + final byte t = type.getType(); + if (isString(type)) { + return createInvoke(appendMethodObjects[0], Const.INVOKEVIRTUAL); + } + switch (t) { + case Const.T_BOOLEAN: + case Const.T_CHAR: + case Const.T_FLOAT: + case Const.T_DOUBLE: + case Const.T_BYTE: + case Const.T_SHORT: + case Const.T_INT: + case Const.T_LONG: + return createInvoke(appendMethodObjects[t], Const.INVOKEVIRTUAL); + case Const.T_ARRAY: + case Const.T_OBJECT: + return createInvoke(appendMethodObjects[1], Const.INVOKEVIRTUAL); + default: + throw new IllegalArgumentException("No append for this type? " + type); + } + } + + /** + * Create conversion operation for two stack operands, this may be an I2C, instruction, e.g., if the operands are basic + * types and CHECKCAST if they are reference types. + */ + public Instruction createCast(final Type srcType, final Type destType) { + if (srcType instanceof BasicType && destType instanceof BasicType) { + final byte dest = destType.getType(); + byte src = srcType.getType(); + if (dest == Const.T_LONG && (src == Const.T_CHAR || src == Const.T_BYTE || src == Const.T_SHORT)) { + src = Const.T_INT; + } + final String name = "org.apache.bcel.generic." + shortNames[src - Const.T_CHAR] + "2" + shortNames[dest - Const.T_CHAR]; + Instruction i = null; + try { + i = (Instruction) Class.forName(name).newInstance(); + } catch (final Exception e) { + throw new IllegalArgumentException("Could not find instruction: " + name, e); + } + return i; + } + if (!(srcType instanceof ReferenceType) || !(destType instanceof ReferenceType)) { + throw new IllegalArgumentException("Cannot cast " + srcType + " to " + destType); + } + if (destType instanceof ArrayType) { + return new CHECKCAST(cp.addArrayClass((ArrayType) destType)); + } + return new CHECKCAST(cp.addClass(((ObjectType) destType).getClassName())); + } + + public CHECKCAST createCheckCast(final ReferenceType t) { + if (t instanceof ArrayType) { + return new CHECKCAST(cp.addArrayClass((ArrayType) t)); + } + return new CHECKCAST(cp.addClass((ObjectType) t)); + } + + /** + * Uses PUSH to push a constant value onto the stack. + * + * @param value must be of type Number, Boolean, Character or String + */ + public Instruction createConstant(final Object value) { + PUSH push; + if (value instanceof Number) { + push = new PUSH(cp, (Number) value); + } else if (value instanceof String) { + push = new PUSH(cp, (String) value); + } else if (value instanceof Boolean) { + push = new PUSH(cp, (Boolean) value); + } else if (value instanceof Character) { + push = new PUSH(cp, (Character) value); + } else { + throw new ClassGenException("Illegal type: " + value.getClass()); + } + return push.getInstruction(); + } + + /** + * Create a field instruction. + * + * @param className name of the accessed class + * @param name name of the referenced field + * @param type type of field + * @param kind how to access, i.e., GETFIELD, PUTFIELD, GETSTATIC, PUTSTATIC + * @see Const + */ + public FieldInstruction createFieldAccess(final String className, final String name, final Type type, final short kind) { + int index; + final String signature = type.getSignature(); + index = cp.addFieldref(className, name, signature); + switch (kind) { + case Const.GETFIELD: + return new GETFIELD(index); + case Const.PUTFIELD: + return new PUTFIELD(index); + case Const.GETSTATIC: + return new GETSTATIC(index); + case Const.PUTSTATIC: + return new PUTSTATIC(index); + default: + throw new IllegalArgumentException("Unknown getfield kind:" + kind); + } + } + + public GETFIELD createGetField(final String className, final String name, final Type t) { + return new GETFIELD(cp.addFieldref(className, name, t.getSignature())); + } + + public GETSTATIC createGetStatic(final String className, final String name, final Type t) { + return new GETSTATIC(cp.addFieldref(className, name, t.getSignature())); + } + + public INSTANCEOF createInstanceOf(final ReferenceType t) { + if (t instanceof ArrayType) { + return new INSTANCEOF(cp.addArrayClass((ArrayType) t)); + } + return new INSTANCEOF(cp.addClass((ObjectType) t)); + } + + private InvokeInstruction createInvoke(final MethodObject m, final short kind) { + return createInvoke(m.className, m.name, m.resultType, m.argTypes, kind); + } + + /** + * Create an invoke instruction. (Except for invokedynamic.) + * + * @param className name of the called class + * @param name name of the called method + * @param retType return type of method + * @param argTypes argument types of method + * @param kind how to invoke, i.e., INVOKEINTERFACE, INVOKESTATIC, INVOKEVIRTUAL, or INVOKESPECIAL + * @see Const + */ + public InvokeInstruction createInvoke(final String className, final String name, final Type retType, final Type[] argTypes, final short kind) { + return createInvoke(className, name, retType, argTypes, kind, kind == Const.INVOKEINTERFACE); + } + + /** + * Create an invoke instruction. (Except for invokedynamic.) + * + * @param className name of the called class + * @param name name of the called method + * @param retType return type of method + * @param argTypes argument types of method + * @param kind how to invoke: INVOKEINTERFACE, INVOKESTATIC, INVOKEVIRTUAL, or INVOKESPECIAL + * @param useInterface force use of InterfaceMethodref + * @return A new InvokeInstruction. + * @since 6.5.0 + */ + public InvokeInstruction createInvoke(final String className, final String name, final Type retType, final Type[] argTypes, final short kind, + final boolean useInterface) { + if (kind != Const.INVOKESPECIAL && kind != Const.INVOKEVIRTUAL && kind != Const.INVOKESTATIC && kind != Const.INVOKEINTERFACE + && kind != Const.INVOKEDYNAMIC) { + throw new IllegalArgumentException("Unknown invoke kind: " + kind); + } + int index; + int nargs = 0; + final String signature = Type.getMethodSignature(retType, argTypes); + for (final Type argType : argTypes) { + nargs += argType.getSize(); + } + if (useInterface) { + index = cp.addInterfaceMethodref(className, name, signature); + } else { + index = cp.addMethodref(className, name, signature); + } + switch (kind) { + case Const.INVOKESPECIAL: + return new INVOKESPECIAL(index); + case Const.INVOKEVIRTUAL: + return new INVOKEVIRTUAL(index); + case Const.INVOKESTATIC: + return new INVOKESTATIC(index); + case Const.INVOKEINTERFACE: + return new INVOKEINTERFACE(index, nargs + 1); + case Const.INVOKEDYNAMIC: + return new INVOKEDYNAMIC(index); + default: + // Can't happen + throw new IllegalStateException("Unknown invoke kind: " + kind); + } + } + + public NEW createNew(final ObjectType t) { + return new NEW(cp.addClass(t)); + } + + public NEW createNew(final String s) { + return createNew(ObjectType.getInstance(s)); + } + + /** + * Create new array of given size and type. + * + * @return an instruction that creates the corresponding array at runtime, i.e. is an AllocationInstruction + */ + public Instruction createNewArray(final Type t, final short dim) { + if (dim == 1) { + if (t instanceof ObjectType) { + return new ANEWARRAY(cp.addClass((ObjectType) t)); + } + if (t instanceof ArrayType) { + return new ANEWARRAY(cp.addArrayClass((ArrayType) t)); + } + return new NEWARRAY(t.getType()); + } + ArrayType at; + if (t instanceof ArrayType) { + at = (ArrayType) t; + } else { + at = new ArrayType(t, dim); + } + return new MULTIANEWARRAY(cp.addArrayClass(at), dim); + } + + /** + * Create a call to the most popular System.out.println() method. + * + * @param s the string to print + */ + public InstructionList createPrintln(final String s) { + final InstructionList il = new InstructionList(); + il.append(createGetStatic("java.lang.System", "out", Type.getType("Ljava/io/PrintStream;"))); + il.append(new PUSH(cp, s)); + final MethodObject methodObject = new MethodObject("java.io.PrintStream", "println", Type.VOID, new Type[]{Type.getType("Ljava/lang/String;")}); + il.append(createInvoke(methodObject, Const.INVOKEVIRTUAL)); + return il; + } + + public PUTFIELD createPutField(final String className, final String name, final Type t) { + return new PUTFIELD(cp.addFieldref(className, name, t.getSignature())); + } + + public PUTSTATIC createPutStatic(final String className, final String name, final Type t) { + return new PUTSTATIC(cp.addFieldref(className, name, t.getSignature())); + } + + public ClassGen getClassGen() { + return cg; + } + + public ConstantPoolGen getConstantPool() { + return cp; + } + + public void setClassGen(final ClassGen c) { + cg = c; + } + + public void setConstantPool(final ConstantPoolGen c) { + cp = c; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/InstructionHandle.java b/src/main/java/haidnor/jvm/bcel/generic/InstructionHandle.java new file mode 100644 index 0000000..1602de7 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/InstructionHandle.java @@ -0,0 +1,300 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.Utility; + +import java.util.*; + +/** + * Instances of this class give users a handle to the instructions contained in an InstructionList. Instruction objects + * may be used more than once within a list, this is useful because it saves memory and may be much faster. + * + * Within an InstructionList an InstructionHandle object is wrapped around all instructions, i.e., it implements a cell + * in a doubly-linked list. From the outside only the next and the previous instruction (handle) are accessible. One can + * traverse the list via an Enumeration returned by InstructionList.elements(). + * + * @see Instruction + * @see BranchHandle + * @see InstructionList + */ +public class InstructionHandle { + + /** + * Empty array. + * + * @since 6.6.0 + */ + public static final InstructionHandle[] EMPTY_ARRAY = {}; + + /** + * Empty array. + */ + static final InstructionTargeter[] EMPTY_INSTRUCTION_TARGETER_ARRAY = {}; + + /** + * Factory method. + */ + static InstructionHandle getInstructionHandle(final Instruction i) { + return new InstructionHandle(i); + } + + private InstructionHandle next; + private InstructionHandle prev; + + private Instruction instruction; + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected int i_position = -1; // byte code offset of instruction + private Set targeters; + + private Map attributes; + + protected InstructionHandle(final Instruction i) { + setInstruction(i); + } + + /** + * Convenience method, simply calls accept() on the contained instruction. + * + * @param v Visitor object + */ + public void accept(final Visitor v) { + instruction.accept(v); + } + + /** + * Add an attribute to an instruction handle. + * + * @param key the key object to store/retrieve the attribute + * @param attr the attribute to associate with this handle + */ + public void addAttribute(final Object key, final Object attr) { + if (attributes == null) { + attributes = new HashMap<>(3); + } + attributes.put(key, attr); + } + + /** + * Does nothing. + * + * @deprecated Does nothing as of 6.3.1. + */ + @Deprecated + protected void addHandle() { + // noop + } + + /** + * Denote this handle is being referenced by t. + */ + public void addTargeter(final InstructionTargeter t) { + if (targeters == null) { + targeters = new HashSet<>(); + } + // if(!targeters.contains(t)) + targeters.add(t); + } + + /** + * Delete contents, i.e., remove user access. + */ + void dispose() { + next = prev = null; + instruction.dispose(); + instruction = null; + i_position = -1; + attributes = null; + removeAllTargeters(); + } + + /** + * Get attribute of an instruction handle. + * + * @param key the key object to store/retrieve the attribute + */ + public Object getAttribute(final Object key) { + if (attributes != null) { + return attributes.get(key); + } + return null; + } + + /** + * @return all attributes associated with this handle + */ + public Collection getAttributes() { + if (attributes == null) { + attributes = new HashMap<>(3); + } + return attributes.values(); + } + + public final Instruction getInstruction() { + return instruction; + } + + public final InstructionHandle getNext() { + return next; + } + + /** + * @return the position, i.e., the byte code offset of the contained instruction. This is accurate only after + * InstructionList.setPositions() has been called. + */ + public int getPosition() { + return i_position; + } + + public final InstructionHandle getPrev() { + return prev; + } + + /** + * @return null, if there are no targeters + */ + public InstructionTargeter[] getTargeters() { + if (!hasTargeters()) { + return EMPTY_INSTRUCTION_TARGETER_ARRAY; + } + final InstructionTargeter[] t = new InstructionTargeter[targeters.size()]; + targeters.toArray(t); + return t; + } + + public boolean hasTargeters() { + return targeters != null && !targeters.isEmpty(); + } + + /** + * Remove all targeters, if any. + */ + public void removeAllTargeters() { + if (targeters != null) { + targeters.clear(); + } + } + + /** + * Delete an attribute of an instruction handle. + * + * @param key the key object to retrieve the attribute + */ + public void removeAttribute(final Object key) { + if (attributes != null) { + attributes.remove(key); + } + } + + /** + * Denote this handle isn't referenced anymore by t. + */ + public void removeTargeter(final InstructionTargeter t) { + if (targeters != null) { + targeters.remove(t); + } + } + + /** + * Replace current instruction contained in this handle. Old instruction is disposed using Instruction.dispose(). + */ + public void setInstruction(final Instruction i) { // Overridden in BranchHandle TODO could be package-protected? + if (i == null) { + throw new ClassGenException("Assigning null to handle"); + } + if (this.getClass() != BranchHandle.class && i instanceof BranchInstruction) { + throw new ClassGenException("Assigning branch instruction " + i + " to plain handle"); + } + if (instruction != null) { + instruction.dispose(); + } + instruction = i; + } + + /** + * @param next the next to set + * @since 6.0 + */ + final InstructionHandle setNext(final InstructionHandle next) { + this.next = next; + return next; + } + + /** + * Set the position, i.e., the byte code offset of the contained instruction. + */ + void setPosition(final int pos) { + i_position = pos; + } + + /** + * @param prev the prev to set + * @since 6.0 + */ + final InstructionHandle setPrev(final InstructionHandle prev) { + this.prev = prev; + return prev; + } + + /** + * Temporarily swap the current instruction, without disturbing anything. Meant to be used by a debugger, implementing + * breakpoints. Current instruction is returned. + *

+ * Warning: if this is used on a BranchHandle then some methods such as getPosition() will still refer to the original + * cached instruction, whereas other BH methods may affect the cache and the replacement instruction. + */ + // See BCEL-273 + // TODO remove this method in any redesign of BCEL + public Instruction swapInstruction(final Instruction i) { + final Instruction oldInstruction = instruction; + instruction = i; + return oldInstruction; + } + + /** + * @return a string representation of the contained instruction. + */ + @Override + public String toString() { + return toString(true); + } + + /** + * @return a (verbose) string representation of the contained instruction. + */ + public String toString(final boolean verbose) { + return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose); + } + + /** + * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable + * length instructions 'setPositions()' performs multiple passes over the instruction list to calculate the correct + * (byte) positions and offsets by calling this function. + * + * @param offset additional offset caused by preceding (variable length) instructions + * @param maxOffset the maximum offset that may be caused by these instructions + * @return additional offset caused by possible change of this instruction's length + */ + protected int updatePosition(final int offset, final int maxOffset) { + i_position += offset; + return 0; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/InstructionList.java b/src/main/java/haidnor/jvm/bcel/generic/InstructionList.java new file mode 100644 index 0000000..92a14d3 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/InstructionList.java @@ -0,0 +1,1185 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.Constant; +import haidnor.jvm.bcel.util.ByteSequence; +import org.apache.commons.lang3.ArrayUtils; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.*; + +/** + * This class is a container for a list of Instruction objects. Instructions can be + * appended, inserted, moved, deleted, etc.. Instructions are being wrapped into + * InstructionHandles objects that are returned upon append/insert operations. They + * give the user (read only) access to the list structure, such that it can be traversed and manipulated in a controlled + * way. + * + * A list is finally dumped to a byte code array with getByteCode. + * + * @see Instruction + * @see InstructionHandle + * @see BranchHandle + */ +public class InstructionList implements Iterable { + + /** + * Find the target instruction (handle) that corresponds to the given target position (byte code offset). + * + * @param ihs array of instruction handles, i.e. il.getInstructionHandles() + * @param pos array of positions corresponding to ihs, i.e. il.getInstructionPositions() + * @param count length of arrays + * @param target target position to search for + * @return target position's instruction handle if available + */ + public static InstructionHandle findHandle(final InstructionHandle[] ihs, final int[] pos, final int count, final int target) { + int l = 0; + int r = count - 1; + /* + * Do a binary search since the pos array is orderd. + */ + do { + final int i = l + r >>> 1; + final int j = pos[i]; + if (j == target) { + return ihs[i]; + } + if (target < j) { + r = i - 1; + } else { + l = i + 1; + } + } while (l <= r); + return null; + } + + private InstructionHandle start; + private InstructionHandle end; + private int length; // number of elements in list + + private int[] bytePositions; // byte code offsets corresponding to instructions + + private List observers; + + /** + * Create (empty) instruction list. + */ + public InstructionList() { + } + + /** + * Create instruction list containing one instruction. + * + * @param i initial instruction + */ + public InstructionList(final BranchInstruction i) { + append(i); + } + + /** + * Initialize instruction list from byte array. + * + * @param code byte array containing the instructions + */ + public InstructionList(final byte[] code) { + int count = 0; // Contains actual length + int[] pos; + InstructionHandle[] ihs; + try (ByteSequence bytes = new ByteSequence(code)) { + ihs = new InstructionHandle[code.length]; + pos = new int[code.length]; // Can't be more than that + /* + * Pass 1: Create an object for each byte code and append them to the list. + */ + while (bytes.available() > 0) { + // Remember byte offset and associate it with the instruction + final int off = bytes.getIndex(); + pos[count] = off; + /* + * Read one instruction from the byte stream, the byte position is set accordingly. + */ + final Instruction i = Instruction.readInstruction(bytes); + InstructionHandle ih; + if (i instanceof BranchInstruction) { + ih = append((BranchInstruction) i); + } else { + ih = append(i); + } + ih.setPosition(off); + ihs[count] = ih; + count++; + } + } catch (final IOException e) { + throw new ClassGenException(e.toString(), e); + } + bytePositions = Arrays.copyOf(pos, count); // Trim to proper size + /* + * Pass 2: Look for BranchInstruction and update their targets, i.e., convert offsets to instruction handles. + */ + for (int i = 0; i < count; i++) { + if (ihs[i] instanceof BranchHandle) { + final BranchInstruction bi = (BranchInstruction) ihs[i].getInstruction(); + int target = bi.getPosition() + bi.getIndex(); /* + * Byte code position: relative -> absolute. + */ + // Search for target position + InstructionHandle ih = findHandle(ihs, pos, count, target); + if (ih == null) { + throw new ClassGenException("Couldn't find target for branch: " + bi); + } + bi.setTarget(ih); // Update target + // If it is a Select instruction, update all branch targets + if (bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH + final Select s = (Select) bi; + final int[] indices = s.getIndices(); + for (int j = 0; j < indices.length; j++) { + target = bi.getPosition() + indices[j]; + ih = findHandle(ihs, pos, count, target); + if (ih == null) { + throw new ClassGenException("Couldn't find target for switch: " + bi); + } + s.setTarget(j, ih); // Update target + } + } + } + } + } + + /** + * Initialize list with (nonnull) compound instruction. Consumes argument list, i.e., it becomes empty. + * + * @param c compound instruction (list) + */ + public InstructionList(final CompoundInstruction c) { + append(c.getInstructionList()); + } + + /** + * Create instruction list containing one instruction. + * + * @param i initial instruction + */ + public InstructionList(final Instruction i) { + append(i); + } + + /** + * Add observer for this object. + */ + public void addObserver(final InstructionListObserver o) { + if (observers == null) { + observers = new ArrayList<>(); + } + observers.add(o); + } + + /** + * Append a branch instruction to the end of this list. + * + * @param i branch instruction to append + * @return branch instruction handle of the appended instruction + */ + public BranchHandle append(final BranchInstruction i) { + final BranchHandle ih = BranchHandle.getBranchHandle(i); + append(ih); + return ih; + } + + /** + * Append a compound instruction. + * + * @param c The composite instruction (containing an InstructionList) + * @return instruction handle of the first appended instruction + */ + public InstructionHandle append(final CompoundInstruction c) { + return append(c.getInstructionList()); + } + + /** + * Append an instruction to the end of this list. + * + * @param i instruction to append + * @return instruction handle of the appended instruction + */ + public InstructionHandle append(final Instruction i) { + final InstructionHandle ih = InstructionHandle.getInstructionHandle(i); + append(ih); + return ih; + } + + /** + * Append a compound instruction, after instruction i. + * + * @param i Instruction in list + * @param c The composite instruction (containing an InstructionList) + * @return instruction handle of the first appended instruction + */ + public InstructionHandle append(final Instruction i, final CompoundInstruction c) { + return append(i, c.getInstructionList()); + } + + /** + * Append a single instruction j after another instruction i, which must be in this list of course! + * + * @param i Instruction in list + * @param j Instruction to append after i in list + * @return instruction handle of the first appended instruction + */ + public InstructionHandle append(final Instruction i, final Instruction j) { + return append(i, new InstructionList(j)); + } + + /** + * Append another list after instruction i contained in this list. Consumes argument list, i.e., it becomes empty. + * + * @param i where to append the instruction list + * @param il Instruction list to append to this one + * @return instruction handle pointing to the first appended instruction + */ + public InstructionHandle append(final Instruction i, final InstructionList il) { + InstructionHandle ih; + if ((ih = findInstruction2(i)) == null) { + throw new ClassGenException("Instruction " + i + " is not contained in this list."); + } + return append(ih, il); + } + + /** + * Append an instruction to the end of this list. + * + * @param ih instruction to append + */ + private void append(final InstructionHandle ih) { + if (isEmpty()) { + start = end = ih; + ih.setNext(ih.setPrev(null)); + } else { + end.setNext(ih); + ih.setPrev(end); + ih.setNext(null); + end = ih; + } + length++; // Update length + } + + /** + * Append an instruction after instruction (handle) ih contained in this list. + * + * @param ih where to append the instruction list + * @param i Instruction to append + * @return instruction handle pointing to the first appended instruction + */ + public BranchHandle append(final InstructionHandle ih, final BranchInstruction i) { + final BranchHandle bh = BranchHandle.getBranchHandle(i); + final InstructionList il = new InstructionList(); + il.append(bh); + append(ih, il); + return bh; + } + + /** + * Append a compound instruction. + * + * @param ih where to append the instruction list + * @param c The composite instruction (containing an InstructionList) + * @return instruction handle of the first appended instruction + */ + public InstructionHandle append(final InstructionHandle ih, final CompoundInstruction c) { + return append(ih, c.getInstructionList()); + } + + /** + * Append an instruction after instruction (handle) ih contained in this list. + * + * @param ih where to append the instruction list + * @param i Instruction to append + * @return instruction handle pointing to the first appended instruction + */ + public InstructionHandle append(final InstructionHandle ih, final Instruction i) { + return append(ih, new InstructionList(i)); + } + + /** + * Append another list after instruction (handle) ih contained in this list. Consumes argument list, i.e., it becomes + * empty. + * + * @param ih where to append the instruction list + * @param il Instruction list to append to this one + * @return instruction handle pointing to the first appended instruction + */ + public InstructionHandle append(final InstructionHandle ih, final InstructionList il) { + if (il == null) { + throw new ClassGenException("Appending null InstructionList"); + } + if (il.isEmpty()) { + return ih; + } + final InstructionHandle next = ih.getNext(); + final InstructionHandle ret = il.start; + ih.setNext(il.start); + il.start.setPrev(ih); + il.end.setNext(next); + if (next != null) { + next.setPrev(il.end); + } else { + end = il.end; // Update end ... + } + length += il.length; // Update length + il.clear(); + return ret; + } + + /** + * Append another list to this one. Consumes argument list, i.e., it becomes empty. + * + * @param il list to append to end of this list + * @return instruction handle of the first appended instruction + */ + public InstructionHandle append(final InstructionList il) { + if (il == null) { + throw new ClassGenException("Appending null InstructionList"); + } + if (il.isEmpty()) { + return null; + } + if (isEmpty()) { + start = il.start; + end = il.end; + length = il.length; + il.clear(); + return start; + } + return append(end, il); // was end.instruction + } + + private void clear() { + start = end = null; + length = 0; + } + + public boolean contains(final Instruction i) { + return findInstruction1(i) != null; + } + + public boolean contains(final InstructionHandle i) { + if (i == null) { + return false; + } + for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { + if (ih == i) { + return true; + } + } + return false; + } + + /** + * @return complete, i.e., deep copy of this list + */ + public InstructionList copy() { + final Map map = new HashMap<>(); + final InstructionList il = new InstructionList(); + /* + * Pass 1: Make copies of all instructions, append them to the new list and associate old instruction references with + * the new ones, i.e., a 1:1 mapping. + */ + for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { + final Instruction i = ih.getInstruction(); + final Instruction c = i.copy(); // Use clone for shallow copy + if (c instanceof BranchInstruction) { + map.put(ih, il.append((BranchInstruction) c)); + } else { + map.put(ih, il.append(c)); + } + } + /* + * Pass 2: Update branch targets. + */ + InstructionHandle ih = start; + InstructionHandle ch = il.start; + while (ih != null) { + final Instruction i = ih.getInstruction(); + final Instruction c = ch.getInstruction(); + if (i instanceof BranchInstruction) { + final BranchInstruction bi = (BranchInstruction) i; + final BranchInstruction bc = (BranchInstruction) c; + final InstructionHandle itarget = bi.getTarget(); // old target + // New target is in hash map + bc.setTarget(map.get(itarget)); + if (bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH + final InstructionHandle[] itargets = ((Select) bi).getTargets(); + final InstructionHandle[] ctargets = ((Select) bc).getTargets(); + for (int j = 0; j < itargets.length; j++) { // Update all targets + ctargets[j] = map.get(itargets[j]); + } + } + } + ih = ih.getNext(); + ch = ch.getNext(); + } + return il; + } + + /** + * Remove instruction from this list. The corresponding Instruction handles must not be reused! + * + * @param i instruction to remove + */ + public void delete(final Instruction i) throws TargetLostException { + InstructionHandle ih; + if ((ih = findInstruction1(i)) == null) { + throw new ClassGenException("Instruction " + i + " is not contained in this list."); + } + delete(ih); + } + + /** + * Remove instructions from instruction 'from' to instruction 'to' contained in this list. The user must ensure that + * 'from' is an instruction before 'to', or risk havoc. The corresponding Instruction handles must not be reused! + * + * @param from where to start deleting (inclusive) + * @param to where to end deleting (inclusive) + */ + public void delete(final Instruction from, final Instruction to) throws TargetLostException { + InstructionHandle fromIh; + InstructionHandle toIh; + if ((fromIh = findInstruction1(from)) == null) { + throw new ClassGenException("Instruction " + from + " is not contained in this list."); + } + if ((toIh = findInstruction2(to)) == null) { + throw new ClassGenException("Instruction " + to + " is not contained in this list."); + } + delete(fromIh, toIh); + } + + /** + * Remove instruction from this list. The corresponding Instruction handles must not be reused! + * + * @param ih instruction (handle) to remove + */ + public void delete(final InstructionHandle ih) throws TargetLostException { + remove(ih.getPrev(), ih.getNext()); + } + + /** + * Remove instructions from instruction 'from' to instruction 'to' contained in this list. The user must ensure that + * 'from' is an instruction before 'to', or risk havoc. The corresponding Instruction handles must not be reused! + * + * @param from where to start deleting (inclusive) + * @param to where to end deleting (inclusive) + */ + public void delete(final InstructionHandle from, final InstructionHandle to) throws TargetLostException { + remove(from.getPrev(), to.getNext()); + } + + /** + * Delete contents of list. Provides better memory utilization, because the system then may reuse the instruction + * handles. This method is typically called right after {@link MethodGen#getMethod()}. + */ + public void dispose() { + // Traverse in reverse order, because ih.next is overwritten + for (InstructionHandle ih = end; ih != null; ih = ih.getPrev()) { + // Causes BranchInstructions to release target and targeters, because it calls dispose() on the contained instruction. + ih.dispose(); + } + clear(); + } + + /** + * Get instruction handle for instruction at byte code position pos. This only works properly, if the list is freshly + * initialized from a byte array or setPositions() has been called before this method. + * + * @param pos byte code position to search for + * @return target position's instruction handle if available + */ + public InstructionHandle findHandle(final int pos) { + final int[] positions = bytePositions; + InstructionHandle ih = start; + for (int i = 0; i < length; i++) { + if (positions[i] == pos) { + return ih; + } + ih = ih.getNext(); + } + return null; + } + + /** + * Search for given Instruction reference, start at beginning of list. + * + * @param i instruction to search for + * @return instruction found on success, null otherwise + */ + private InstructionHandle findInstruction1(final Instruction i) { + for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { + if (ih.getInstruction() == i) { + return ih; + } + } + return null; + } + + /** + * Search for given Instruction reference, start at end of list + * + * @param i instruction to search for + * @return instruction found on success, null otherwise + */ + private InstructionHandle findInstruction2(final Instruction i) { + for (InstructionHandle ih = end; ih != null; ih = ih.getPrev()) { + if (ih.getInstruction() == i) { + return ih; + } + } + return null; + } + + /** + * When everything is finished, use this method to convert the instruction list into an array of bytes. + * + * @return the byte code ready to be dumped + */ + public byte[] getByteCode() { + // Update position indices of instructions + setPositions(); + final ByteArrayOutputStream b = new ByteArrayOutputStream(); + final DataOutputStream out = new DataOutputStream(b); + try { + for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { + final Instruction i = ih.getInstruction(); + i.dump(out); // Traverse list + } + out.flush(); + } catch (final IOException e) { + System.err.println(e); + return ArrayUtils.EMPTY_BYTE_ARRAY; + } + return b.toByteArray(); + } + + /** + * @return end of list + */ + public InstructionHandle getEnd() { + return end; + } + + /** + * @return array containing all instructions (handles) + */ + public InstructionHandle[] getInstructionHandles() { + final InstructionHandle[] ihs = new InstructionHandle[length]; + InstructionHandle ih = start; + for (int i = 0; i < length; i++) { + ihs[i] = ih; + ih = ih.getNext(); + } + return ihs; + } + + /** + * Get positions (offsets) of all instructions in the list. This relies on that the list has been freshly created from + * an byte code array, or that setPositions() has been called. Otherwise this may be inaccurate. + * + * @return array containing all instruction's offset in byte code + */ + public int[] getInstructionPositions() { + return bytePositions; + } + + /** + * @return an array of instructions without target information for branch instructions. + */ + public Instruction[] getInstructions() { + final List instructions = new ArrayList<>(); + try (ByteSequence bytes = new ByteSequence(getByteCode())) { + while (bytes.available() > 0) { + instructions.add(Instruction.readInstruction(bytes)); + } + } catch (final IOException e) { + throw new ClassGenException(e.toString(), e); + } + return instructions.toArray(Instruction.EMPTY_ARRAY); + } + + /** + * @return length of list (Number of instructions, not bytes) + */ + public int getLength() { + return length; + } + + /** + * @return start of list + */ + public InstructionHandle getStart() { + return start; + } + + /** + * Insert a branch instruction at start of this list. + * + * @param i branch instruction to insert + * @return branch instruction handle of the appended instruction + */ + public BranchHandle insert(final BranchInstruction i) { + final BranchHandle ih = BranchHandle.getBranchHandle(i); + insert(ih); + return ih; + } + + /** + * Insert a compound instruction. + * + * @param c The composite instruction (containing an InstructionList) + * @return instruction handle of the first inserted instruction + */ + public InstructionHandle insert(final CompoundInstruction c) { + return insert(c.getInstructionList()); + } + + /** + * Insert an instruction at start of this list. + * + * @param i instruction to insert + * @return instruction handle of the inserted instruction + */ + public InstructionHandle insert(final Instruction i) { + final InstructionHandle ih = InstructionHandle.getInstructionHandle(i); + insert(ih); + return ih; + } + + /** + * Insert a compound instruction before instruction i. + * + * @param i Instruction in list + * @param c The composite instruction (containing an InstructionList) + * @return instruction handle of the first inserted instruction + */ + public InstructionHandle insert(final Instruction i, final CompoundInstruction c) { + return insert(i, c.getInstructionList()); + } + + /** + * Insert a single instruction j before another instruction i, which must be in this list of course! + * + * @param i Instruction in list + * @param j Instruction to insert before i in list + * @return instruction handle of the first inserted instruction + */ + public InstructionHandle insert(final Instruction i, final Instruction j) { + return insert(i, new InstructionList(j)); + } + + /** + * Insert another list before Instruction i contained in this list. Consumes argument list, i.e., it becomes empty. + * + * @param i where to append the instruction list + * @param il Instruction list to insert + * @return instruction handle pointing to the first inserted instruction, i.e., il.getStart() + */ + public InstructionHandle insert(final Instruction i, final InstructionList il) { + InstructionHandle ih; + if ((ih = findInstruction1(i)) == null) { + throw new ClassGenException("Instruction " + i + " is not contained in this list."); + } + return insert(ih, il); + } + + /** + * Insert an instruction at start of this list. + * + * @param ih instruction to insert + */ + private void insert(final InstructionHandle ih) { + if (isEmpty()) { + start = end = ih; + ih.setNext(ih.setPrev(null)); + } else { + start.setPrev(ih); + ih.setNext(start); + ih.setPrev(null); + start = ih; + } + length++; + } + + /** + * Insert an instruction before instruction (handle) ih contained in this list. + * + * @param ih where to insert to the instruction list + * @param i Instruction to insert + * @return instruction handle of the first inserted instruction + */ + public BranchHandle insert(final InstructionHandle ih, final BranchInstruction i) { + final BranchHandle bh = BranchHandle.getBranchHandle(i); + final InstructionList il = new InstructionList(); + il.append(bh); + insert(ih, il); + return bh; + } + + /** + * Insert a compound instruction. + * + * @param ih where to insert the instruction list + * @param c The composite instruction (containing an InstructionList) + * @return instruction handle of the first inserted instruction + */ + public InstructionHandle insert(final InstructionHandle ih, final CompoundInstruction c) { + return insert(ih, c.getInstructionList()); + } + + /** + * Insert an instruction before instruction (handle) ih contained in this list. + * + * @param ih where to insert to the instruction list + * @param i Instruction to insert + * @return instruction handle of the first inserted instruction + */ + public InstructionHandle insert(final InstructionHandle ih, final Instruction i) { + return insert(ih, new InstructionList(i)); + } + + /** + * Insert another list before Instruction handle ih contained in this list. Consumes argument list, i.e., it becomes + * empty. + * + * @param ih where to append the instruction list + * @param il Instruction list to insert + * @return instruction handle of the first inserted instruction + */ + public InstructionHandle insert(final InstructionHandle ih, final InstructionList il) { + if (il == null) { + throw new ClassGenException("Inserting null InstructionList"); + } + if (il.isEmpty()) { + return ih; + } + final InstructionHandle prev = ih.getPrev(); + final InstructionHandle ret = il.start; + ih.setPrev(il.end); + il.end.setNext(ih); + il.start.setPrev(prev); + if (prev != null) { + prev.setNext(il.start); + } else { + start = il.start; // Update start ... + } + length += il.length; // Update length + il.clear(); + return ret; + } + + /** + * Insert another list. + * + * @param il list to insert before start of this list + * @return instruction handle of the first inserted instruction + */ + public InstructionHandle insert(final InstructionList il) { + if (isEmpty()) { + append(il); // Code is identical for this case + return start; + } + return insert(start, il); + } + + /** + * Test for empty list. + */ + public boolean isEmpty() { + return start == null; + } // && end == null + + /** + * @return iterator that lists all instructions (handles) + */ + @Override + public Iterator iterator() { + return new Iterator() { + + private InstructionHandle ih = start; + + @Override + public boolean hasNext() { + return ih != null; + } + + @Override + public InstructionHandle next() throws NoSuchElementException { + if (ih == null) { + throw new NoSuchElementException(); + } + final InstructionHandle i = ih; + ih = ih.getNext(); + return i; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Move a single instruction (handle) to a new location. + * + * @param ih moved instruction + * @param target new location of moved instruction + */ + public void move(final InstructionHandle ih, final InstructionHandle target) { + move(ih, ih, target); + } + + /** + * Take all instructions (handles) from "start" to "end" and append them after the new location "target". Of course, + * "end" must be after "start" and target must not be located withing this range. If you want to move something to the + * start of the list use null as value for target. + *

+ * Any instruction targeters pointing to handles within the block, keep their targets. + *

+ * + * @param start of moved block + * @param end of moved block + * @param target of moved block + */ + public void move(final InstructionHandle start, final InstructionHandle end, final InstructionHandle target) { + // Step 1: Check constraints + if (start == null || end == null) { + throw new ClassGenException("Invalid null handle: From " + start + " to " + end); + } + if (target == start || target == end) { + throw new ClassGenException("Invalid range: From " + start + " to " + end + " contains target " + target); + } + for (InstructionHandle ih = start; ih != end.getNext(); ih = ih.getNext()) { + if (ih == null) { + throw new ClassGenException("Invalid range: From " + start + " to " + end); + } + if (ih == target) { + throw new ClassGenException("Invalid range: From " + start + " to " + end + " contains target " + target); + } + } + // Step 2: Temporarily remove the given instructions from the list + final InstructionHandle prev = start.getPrev(); + InstructionHandle next = end.getNext(); + if (prev != null) { + prev.setNext(next); + } else { + this.start = next; + } + if (next != null) { + next.setPrev(prev); + } else { + this.end = prev; + } + start.setPrev(end.setNext(null)); + // Step 3: append after target + if (target == null) { // append to start of list + if (this.start != null) { + this.start.setPrev(end); + } + end.setNext(this.start); + this.start = start; + } else { + next = target.getNext(); + target.setNext(start); + start.setPrev(target); + end.setNext(next); + if (next != null) { + next.setPrev(end); + } else { + this.end = end; + } + } + } + + /** + * Redirect all references from oldTarget to newTarget, i.e., update targets of branch instructions. + * + * @param oldTarget the old target instruction handle + * @param newTarget the new target instruction handle + */ + public void redirectBranches(final InstructionHandle oldTarget, final InstructionHandle newTarget) { + for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { + final Instruction i = ih.getInstruction(); + if (i instanceof BranchInstruction) { + final BranchInstruction b = (BranchInstruction) i; + final InstructionHandle target = b.getTarget(); + if (target == oldTarget) { + b.setTarget(newTarget); + } + if (b instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH + final InstructionHandle[] targets = ((Select) b).getTargets(); + for (int j = 0; j < targets.length; j++) { + if (targets[j] == oldTarget) { + ((Select) b).setTarget(j, newTarget); + } + } + } + } + } + } + + /** + * Redirect all references of exception handlers from oldTarget to newTarget. + * + * @param exceptions array of exception handlers + * @param oldTarget the old target instruction handle + * @param newTarget the new target instruction handle + * @see MethodGen + */ + public void redirectExceptionHandlers(final CodeExceptionGen[] exceptions, final InstructionHandle oldTarget, final InstructionHandle newTarget) { + for (final CodeExceptionGen exception : exceptions) { + if (exception.getStartPC() == oldTarget) { + exception.setStartPC(newTarget); + } + if (exception.getEndPC() == oldTarget) { + exception.setEndPC(newTarget); + } + if (exception.getHandlerPC() == oldTarget) { + exception.setHandlerPC(newTarget); + } + } + } + + /** + * Redirect all references of local variables from oldTarget to newTarget. + * + * @param lg array of local variables + * @param oldTarget the old target instruction handle + * @param newTarget the new target instruction handle + * @see MethodGen + */ + public void redirectLocalVariables(final LocalVariableGen[] lg, final InstructionHandle oldTarget, final InstructionHandle newTarget) { + for (final LocalVariableGen element : lg) { + final InstructionHandle start = element.getStart(); + final InstructionHandle end = element.getEnd(); + if (start == oldTarget) { + element.setStart(newTarget); + } + if (end == oldTarget) { + element.setEnd(newTarget); + } + } + } + + /** + * Remove from instruction 'prev' to instruction 'next' both contained in this list. Throws TargetLostException when one + * of the removed instruction handles is still being targeted. + * + * @param prev where to start deleting (predecessor, exclusive) + * @param next where to end deleting (successor, exclusive) + */ + private void remove(final InstructionHandle prev, InstructionHandle next) throws TargetLostException { + InstructionHandle first; + InstructionHandle last; // First and last deleted instruction + if (prev == null && next == null) { + first = start; + last = end; + start = end = null; + } else { + if (prev == null) { // At start of list + first = start; + start = next; + } else { + first = prev.getNext(); + prev.setNext(next); + } + if (next == null) { // At end of list + last = end; + end = prev; + } else { + last = next.getPrev(); + next.setPrev(prev); + } + } + first.setPrev(null); // Completely separated from rest of list + last.setNext(null); + final List targetList = new ArrayList<>(); + for (InstructionHandle ih = first; ih != null; ih = ih.getNext()) { + ih.getInstruction().dispose(); // e.g. BranchInstructions release their targets + } + final StringBuilder buf = new StringBuilder("{ "); + for (InstructionHandle ih = first; ih != null; ih = next) { + next = ih.getNext(); + length--; + if (ih.hasTargeters()) { // Still got targeters? + targetList.add(ih); + buf.append(ih.toString(true)).append(" "); + ih.setNext(ih.setPrev(null)); + } else { + ih.dispose(); + } + } + buf.append("}"); + if (!targetList.isEmpty()) { + throw new TargetLostException(targetList.toArray(InstructionHandle.EMPTY_ARRAY), buf.toString()); + } + } + + /** + * Remove observer for this object. + */ + public void removeObserver(final InstructionListObserver o) { + if (observers != null) { + observers.remove(o); + } + } + + /** + * Replace all references to the old constant pool with references to the new constant pool + */ + public void replaceConstantPool(final ConstantPoolGen oldCp, final ConstantPoolGen newCp) { + for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { + final Instruction i = ih.getInstruction(); + if (i instanceof CPInstruction) { + final CPInstruction ci = (CPInstruction) i; + final Constant c = oldCp.getConstant(ci.getIndex()); + ci.setIndex(newCp.addConstant(c, oldCp)); + } + } + } + + public void setPositions() { // TODO could be package-protected? (some test code would need to be repackaged) + setPositions(false); + } + + /** + * Give all instructions their position number (offset in byte stream), i.e., make the list ready to be dumped. + * + * @param check Perform sanity checks, e.g. if all targeted instructions really belong to this list + */ + public void setPositions(final boolean check) { // called by code in other packages + int maxAdditionalBytes = 0; + int additionalBytes = 0; + int index = 0; + int count = 0; + final int[] pos = new int[length]; + /* + * Pass 0: Sanity checks + */ + if (check) { + for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { + final Instruction i = ih.getInstruction(); + if (i instanceof BranchInstruction) { // target instruction within list? + Instruction inst = ((BranchInstruction) i).getTarget().getInstruction(); + if (!contains(inst)) { + throw new ClassGenException("Branch target of " + Const.getOpcodeName(i.getOpcode()) + ":" + inst + " not in instruction list"); + } + if (i instanceof Select) { + final InstructionHandle[] targets = ((Select) i).getTargets(); + for (final InstructionHandle target : targets) { + inst = target.getInstruction(); + if (!contains(inst)) { + throw new ClassGenException("Branch target of " + Const.getOpcodeName(i.getOpcode()) + ":" + inst + " not in instruction list"); + } + } + } + if (!(ih instanceof BranchHandle)) { + throw new ClassGenException( + "Branch instruction " + Const.getOpcodeName(i.getOpcode()) + ":" + inst + " not contained in BranchHandle."); + } + } + } + } + /* + * Pass 1: Set position numbers and sum up the maximum number of bytes an instruction may be shifted. + */ + for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { + final Instruction i = ih.getInstruction(); + ih.setPosition(index); + pos[count++] = index; + /* + * Get an estimate about how many additional bytes may be added, because BranchInstructions may have variable length + * depending on the target offset (short vs. int) or alignment issues (TABLESWITCH and LOOKUPSWITCH). + */ + switch (i.getOpcode()) { + case Const.JSR: + case Const.GOTO: + maxAdditionalBytes += 2; + break; + case Const.TABLESWITCH: + case Const.LOOKUPSWITCH: + maxAdditionalBytes += 3; + break; + } + index += i.getLength(); + } + /* + * Pass 2: Expand the variable-length (Branch)Instructions depending on the target offset (short or int) and ensure that + * branch targets are within this list. + */ + for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { + additionalBytes += ih.updatePosition(additionalBytes, maxAdditionalBytes); + } + /* + * Pass 3: Update position numbers (which may have changed due to the preceding expansions), like pass 1. + */ + index = count = 0; + for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { + final Instruction i = ih.getInstruction(); + ih.setPosition(index); + pos[count++] = index; + index += i.getLength(); + } + bytePositions = new int[count]; // Trim to proper size + System.arraycopy(pos, 0, bytePositions, 0, count); + } + + /** + * @return length of list (Number of instructions, not bytes) + */ + public int size() { + return length; + } + + @Override + public String toString() { + return toString(true); + } + + /** + * @param verbose toggle output format + * @return String containing all instructions in this list. + */ + public String toString(final boolean verbose) { + final StringBuilder buf = new StringBuilder(); + for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { + buf.append(ih.toString(verbose)).append("\n"); + } + return buf.toString(); + } + + /** + * Call notify() method on all observers. This method is not called automatically whenever the state has changed, but + * has to be called by the user after he has finished editing the object. + */ + public void update() { + if (observers != null) { + for (final InstructionListObserver observer : observers) { + observer.notify(this); + } + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/InstructionListObserver.java b/src/main/java/haidnor/jvm/bcel/generic/InstructionListObserver.java new file mode 100644 index 0000000..d4c6b14 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/InstructionListObserver.java @@ -0,0 +1,26 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Implement this interface if you're interested in changes to an InstructionList object and register yourself with + * addObserver(). + */ +public interface InstructionListObserver { + + void notify(InstructionList list); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/InstructionTargeter.java b/src/main/java/haidnor/jvm/bcel/generic/InstructionTargeter.java new file mode 100644 index 0000000..de949a5 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/InstructionTargeter.java @@ -0,0 +1,44 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denote that a class targets InstructionHandles within an InstructionList. Namely the following implementers: + * + * @see BranchHandle + * @see LocalVariableGen + * @see CodeExceptionGen + */ +public interface InstructionTargeter { + + // static final InstructionTargeter[] EMPTY_ARRAY = new InstructionTargeter[0]; + + /** + * Checks whether this targeter targets the specified instruction handle. + */ + boolean containsTarget(InstructionHandle ih); + + /** + * Replaces the target of this targeter from this old handle to the new handle. + * + * @param oldIh the old handle + * @param newIh the new handle + * @throws ClassGenException if oldIh is not targeted by this object + */ + void updateTarget(InstructionHandle oldIh, InstructionHandle newIh) throws ClassGenException; +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/InvokeInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/InvokeInstruction.java new file mode 100644 index 0000000..41ebd89 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/InvokeInstruction.java @@ -0,0 +1,140 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.Constant; +import haidnor.jvm.bcel.classfile.ConstantCP; +import haidnor.jvm.bcel.classfile.ConstantPool; +import haidnor.jvm.bcel.classfile.Utility; + +import java.util.StringTokenizer; + +/** + * Super class for the INVOKExxx family of instructions. + */ +public abstract class InvokeInstruction extends FieldOrMethod implements ExceptionThrower, StackConsumer, StackProducer { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + InvokeInstruction() { + } + + /** + * @param index to constant pool + */ + protected InvokeInstruction(final short opcode, final int index) { + super(opcode, index); + } + + /** + * Also works for instructions whose stack effect depends on the constant pool entry they reference. + * + * @return Number of words consumed from stack by this instruction + */ + @Override + public int consumeStack(final ConstantPoolGen cpg) { + int sum; + if (super.getOpcode() == Const.INVOKESTATIC || super.getOpcode() == Const.INVOKEDYNAMIC) { + sum = 0; + } else { + sum = 1; // this reference + } + + final String signature = getSignature(cpg); + sum += Type.getArgumentTypesSize(signature); + return sum; + } + + /** + * @return argument types of referenced method. + */ + public Type[] getArgumentTypes(final ConstantPoolGen cpg) { + return Type.getArgumentTypes(getSignature(cpg)); + } + + /** + * This overrides the deprecated version as we know here that the referenced class may legally be an array. + * + * @return name of the referenced class/interface + * @throws IllegalArgumentException if the referenced class is an array (this should not happen) + */ + @Override + public String getClassName(final ConstantPoolGen cpg) { + final ConstantPool cp = cpg.getConstantPool(); + final ConstantCP cmr = (ConstantCP) cp.getConstant(super.getIndex()); + final String className = cp.getConstantString(cmr.getClassIndex(), Const.CONSTANT_Class); + return Utility.pathToPackage(className); + } + + /** + * @return name of referenced method. + */ + public String getMethodName(final ConstantPoolGen cpg) { + return getName(cpg); + } + + /** + * @return return type of referenced method. + */ + public Type getReturnType(final ConstantPoolGen cpg) { + return Type.getReturnType(getSignature(cpg)); + } + + /** + * @return return type of referenced method. + */ + @Override + public Type getType(final ConstantPoolGen cpg) { + return getReturnType(cpg); + } + + /** + * Also works for instructions whose stack effect depends on the constant pool entry they reference. + * + * @return Number of words produced onto stack by this instruction + */ + @Override + public int produceStack(final ConstantPoolGen cpg) { + final String signature = getSignature(cpg); + return Type.getReturnTypeSize(signature); + } + + /** + * @return mnemonic for instruction with symbolic references resolved + */ + @Override + public String toString(final ConstantPool cp) { + final Constant c = cp.getConstant(super.getIndex()); + final StringTokenizer tok = new StringTokenizer(cp.constantToString(c)); + + final String opcodeName = Const.getOpcodeName(super.getOpcode()); + + final StringBuilder sb = new StringBuilder(opcodeName); + if (tok.hasMoreTokens()) { + sb.append(" "); + sb.append(Utility.packageToPath(tok.nextToken())); + if (tok.hasMoreTokens()) { + sb.append(tok.nextToken()); + } + } + + return sb.toString(); + } + +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/JSR.java b/src/main/java/haidnor/jvm/bcel/generic/JSR.java new file mode 100644 index 0000000..d29ba16 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/JSR.java @@ -0,0 +1,83 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * JSR - Jump to subroutine + */ +public class JSR extends JsrInstruction implements VariableLengthInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + JSR() { + } + + public JSR(final InstructionHandle target) { + super(Const.JSR, target); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitVariableLengthInstruction(this); + v.visitBranchInstruction(this); + v.visitJsrInstruction(this); + v.visitJSR(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + super.setIndex(getTargetOffset()); + if (super.getOpcode() == Const.JSR) { + super.dump(out); + } else { // JSR_W + super.setIndex(getTargetOffset()); + out.writeByte(super.getOpcode()); + out.writeInt(super.getIndex()); + } + } + + @Override + protected int updatePosition(final int offset, final int maxOffset) { + final int i = getTargetOffset(); // Depending on old position value + setPosition(getPosition() + offset); // Position may be shifted by preceding expansions + if (Math.abs(i) >= Short.MAX_VALUE - maxOffset) { // to large for short (estimate) + super.setOpcode(Const.JSR_W); + final short oldLength = (short) super.getLength(); + super.setLength(5); + return super.getLength() - oldLength; + } + return 0; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/JSR_W.java b/src/main/java/haidnor/jvm/bcel/generic/JSR_W.java new file mode 100644 index 0000000..f5c1926 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/JSR_W.java @@ -0,0 +1,75 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * JSR_W - Jump to subroutine + */ +public class JSR_W extends JsrInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + JSR_W() { + } + + public JSR_W(final InstructionHandle target) { + super(Const.JSR_W, target); + super.setLength(5); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitBranchInstruction(this); + v.visitJsrInstruction(this); + v.visitJSR_W(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + super.setIndex(getTargetOffset()); + out.writeByte(super.getOpcode()); + out.writeInt(super.getIndex()); + } + + /** + * Read needed data (e.g. index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + super.setIndex(bytes.readInt()); + super.setLength(5); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/JsrInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/JsrInstruction.java new file mode 100644 index 0000000..19b5e13 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/JsrInstruction.java @@ -0,0 +1,69 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Super class for JSR - Jump to subroutine + */ +public abstract class JsrInstruction extends BranchInstruction implements UnconditionalBranch, TypedInstruction, StackProducer { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + JsrInstruction() { + } + + JsrInstruction(final short opcode, final InstructionHandle target) { + super(opcode, target); + } + + /** + * @return return address type + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return new ReturnaddressType(physicalSuccessor()); + } + + /** + * Returns an InstructionHandle to the physical successor of this JsrInstruction. For this method to work, this + * JsrInstruction object must not be shared between multiple InstructionHandle objects! Formally, there must not be + * InstructionHandle objects i, j where i != j and i.getInstruction() == this == j.getInstruction(). + * + * @return an InstructionHandle to the "next" instruction that will be executed when RETurned from a subroutine. + */ + public InstructionHandle physicalSuccessor() { + InstructionHandle ih = super.getTarget(); + // Rewind! + while (ih.getPrev() != null) { + ih = ih.getPrev(); + } + // Find the handle for "this" JsrInstruction object. + while (ih.getInstruction() != this) { + ih = ih.getNext(); + } + final InstructionHandle toThis = ih; + while (ih != null) { + ih = ih.getNext(); + if (ih != null && ih.getInstruction() == this) { + throw new IllegalStateException("physicalSuccessor() called on a shared JsrInstruction."); + } + } + // Return the physical successor + return toThis.getNext(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/L2D.java b/src/main/java/haidnor/jvm/bcel/generic/L2D.java new file mode 100644 index 0000000..c65656f --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/L2D.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * L2D - Convert long to double + * + *
+ * Stack: ..., value.word1, value.word2 -> ..., result.word1, result.word2
+ * 
+ */ +public class L2D extends ConversionInstruction { + + public L2D() { + super(Const.L2D); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitL2D(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/L2F.java b/src/main/java/haidnor/jvm/bcel/generic/L2F.java new file mode 100644 index 0000000..0039978 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/L2F.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * L2F - Convert long to float + * + *
+ * Stack: ..., value.word1, value.word2 -> ..., result
+ * 
+ */ +public class L2F extends ConversionInstruction { + + public L2F() { + super(Const.L2F); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitL2F(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/L2I.java b/src/main/java/haidnor/jvm/bcel/generic/L2I.java new file mode 100644 index 0000000..9264946 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/L2I.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * L2I - Convert long to int + * + *
+ * Stack: ..., value.word1, value.word2 -> ..., result
+ * 
+ */ +public class L2I extends ConversionInstruction { + + public L2I() { + super(Const.L2I); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitConversionInstruction(this); + v.visitL2I(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LADD.java b/src/main/java/haidnor/jvm/bcel/generic/LADD.java new file mode 100644 index 0000000..a633bfb --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LADD.java @@ -0,0 +1,50 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LADD - Add longs + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 ->
+ * 
+ * + * ..., result.word1, result.word2 + */ +public class LADD extends ArithmeticInstruction { + + public LADD() { + super(Const.LADD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLADD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LALOAD.java b/src/main/java/haidnor/jvm/bcel/generic/LALOAD.java new file mode 100644 index 0000000..d0a8374 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LALOAD.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LALOAD - Load long from array + * + *
+ * Stack: ..., arrayref, index -> ..., value1, value2
+ * 
+ */ +public class LALOAD extends ArrayInstruction implements StackProducer { + + /** + * Load long from array + */ + public LALOAD() { + super(Const.LALOAD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitLALOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LAND.java b/src/main/java/haidnor/jvm/bcel/generic/LAND.java new file mode 100644 index 0000000..8c11081 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LAND.java @@ -0,0 +1,50 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LAND - Bitwise AND longs + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 ->
+ * 
+ * + * ..., result.word1, result.word2 + */ +public class LAND extends ArithmeticInstruction { + + public LAND() { + super(Const.LAND); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLAND(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LASTORE.java b/src/main/java/haidnor/jvm/bcel/generic/LASTORE.java new file mode 100644 index 0000000..7ce22e7 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LASTORE.java @@ -0,0 +1,51 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LASTORE - Store into long array + * + *
+ * Stack: ..., arrayref, index, value.word1, value.word2 -> ...
+ * 
+ */ +public class LASTORE extends ArrayInstruction implements StackConsumer { + + /** + * Store long into array + */ + public LASTORE() { + super(Const.LASTORE); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitLASTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LCMP.java b/src/main/java/haidnor/jvm/bcel/generic/LCMP.java new file mode 100644 index 0000000..487bf67 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LCMP.java @@ -0,0 +1,56 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LCMP - Compare longs: + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 -> ..., result <= -1, 0, 1>
+ * 
+ * + */ +public class LCMP extends Instruction implements TypedInstruction, StackProducer, StackConsumer { + + public LCMP() { + super(Const.LCMP, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitLCMP(this); + } + + /** + * @return Type.LONG + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.LONG; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LCONST.java b/src/main/java/haidnor/jvm/bcel/generic/LCONST.java new file mode 100644 index 0000000..942cb37 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LCONST.java @@ -0,0 +1,78 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LCONST - Push 0 or 1, other values cause an exception + * + *
+ * Stack: ... -> ...,
+ * 
+ */ +public class LCONST extends Instruction implements ConstantPushInstruction { + + private final long value; + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + LCONST() { + this(0); + } + + public LCONST(final long l) { + super(Const.LCONST_0, (short) 1); + if (l == 0) { + super.setOpcode(Const.LCONST_0); + } else if (l == 1) { + super.setOpcode(Const.LCONST_1); + } else { + throw new ClassGenException("LCONST can be used only for 0 and 1: " + l); + } + value = l; + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitPushInstruction(this); + v.visitStackProducer(this); + v.visitTypedInstruction(this); + v.visitConstantPushInstruction(this); + v.visitLCONST(this); + } + + /** + * @return Type.LONG + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.LONG; + } + + @Override + public Number getValue() { + return Long.valueOf(value); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LDC.java b/src/main/java/haidnor/jvm/bcel/generic/LDC.java new file mode 100644 index 0000000..1b1fa6d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LDC.java @@ -0,0 +1,147 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; +import haidnor.jvm.bcel.classfile.*; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * LDC - Push item from constant pool. + * + *
+ * Stack: ... -> ..., item
+ * 
+ */ +public class LDC extends CPInstruction implements PushInstruction, ExceptionThrower { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + LDC() { + } + + public LDC(final int index) { + super(Const.LDC_W, index); + setSize(); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitPushInstruction(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitCPInstruction(this); + v.visitLDC(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(super.getOpcode()); + if (super.getLength() == 2) { // TODO useless check? + out.writeByte(super.getIndex()); + } else { + out.writeShort(super.getIndex()); + } + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_STRING_RESOLUTION); + } + + @Override + public Type getType(final ConstantPoolGen cpg) { + switch (cpg.getConstantPool().getConstant(super.getIndex()).getTag()) { + case Const.CONSTANT_String: + return Type.STRING; + case Const.CONSTANT_Float: + return Type.FLOAT; + case Const.CONSTANT_Integer: + return Type.INT; + case Const.CONSTANT_Class: + return Type.CLASS; + default: // Never reached + throw new IllegalArgumentException("Unknown or invalid constant type at " + super.getIndex()); + } + } + + public Object getValue(final ConstantPoolGen cpg) { + Constant c = cpg.getConstantPool().getConstant(super.getIndex()); + switch (c.getTag()) { + case Const.CONSTANT_String: + final int i = ((ConstantString) c).getStringIndex(); + c = cpg.getConstantPool().getConstant(i); + return ((ConstantUtf8) c).getBytes(); + case Const.CONSTANT_Float: + return Float.valueOf(((ConstantFloat) c).getBytes()); + case Const.CONSTANT_Integer: + return Integer.valueOf(((ConstantInteger) c).getBytes()); + case Const.CONSTANT_Class: + final int nameIndex = ((ConstantClass) c).getNameIndex(); + c = cpg.getConstantPool().getConstant(nameIndex); + return Type.getType(((ConstantUtf8) c).getBytes()); + default: // Never reached + throw new IllegalArgumentException("Unknown or invalid constant type at " + super.getIndex()); + } + } + + /** + * Read needed data (e.g. index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + super.setLength(2); + super.setIndex(bytes.readUnsignedByte()); + } + + /** + * Set the index to constant pool and adjust size. + */ + @Override + public final void setIndex(final int index) { + super.setIndex(index); + setSize(); + } + + // Adjust to proper size + protected final void setSize() { + if (super.getIndex() <= Const.MAX_BYTE) { // Fits in one byte? + super.setOpcode(Const.LDC); + super.setLength(2); + } else { + super.setOpcode(Const.LDC_W); + super.setLength(3); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LDC2_W.java b/src/main/java/haidnor/jvm/bcel/generic/LDC2_W.java new file mode 100644 index 0000000..579b0ab --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LDC2_W.java @@ -0,0 +1,81 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.Constant; +import haidnor.jvm.bcel.classfile.ConstantDouble; +import haidnor.jvm.bcel.classfile.ConstantLong; + +/** + * LDC2_W - Push long or double from constant pool + * + *
+ * Stack: ... -> ..., item.word1, item.word2
+ * 
+ */ +public class LDC2_W extends CPInstruction implements PushInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + LDC2_W() { + } + + public LDC2_W(final int index) { + super(Const.LDC2_W, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitPushInstruction(this); + v.visitTypedInstruction(this); + v.visitCPInstruction(this); + v.visitLDC2_W(this); + } + + @Override + public Type getType(final ConstantPoolGen cpg) { + switch (cpg.getConstantPool().getConstant(super.getIndex()).getTag()) { + case Const.CONSTANT_Long: + return Type.LONG; + case Const.CONSTANT_Double: + return Type.DOUBLE; + default: // Never reached + throw new IllegalArgumentException("Unknown constant type " + super.getOpcode()); + } + } + + public Number getValue(final ConstantPoolGen cpg) { + final Constant c = cpg.getConstantPool().getConstant(super.getIndex()); + switch (c.getTag()) { + case Const.CONSTANT_Long: + return Long.valueOf(((ConstantLong) c).getBytes()); + case Const.CONSTANT_Double: + return Double.valueOf(((ConstantDouble) c).getBytes()); + default: // Never reached + throw new IllegalArgumentException("Unknown or invalid constant type at " + super.getIndex()); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LDC_W.java b/src/main/java/haidnor/jvm/bcel/generic/LDC_W.java new file mode 100644 index 0000000..0965866 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LDC_W.java @@ -0,0 +1,53 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.IOException; + +/** + * LDC_W - Push item from constant pool (wide index) + * + *
+ * Stack: ... -> ..., item.word1, item.word2
+ * 
+ */ +public class LDC_W extends LDC { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + LDC_W() { + } + + public LDC_W(final int index) { + super(index); + } + + /** + * Read needed data (i.e., index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + setIndex(bytes.readUnsignedShort()); + // Override just in case it has been changed + super.setOpcode(Const.LDC_W); + super.setLength(3); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LDIV.java b/src/main/java/haidnor/jvm/bcel/generic/LDIV.java new file mode 100644 index 0000000..be20296 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LDIV.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * LDIV - Divide longs + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 ->
+ * 
+ * + * ..., result.word1, result.word2 + */ +public class LDIV extends ArithmeticInstruction implements ExceptionThrower { + + public LDIV() { + super(Const.LDIV); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLDIV(this); + } + + @Override + public Class[] getExceptions() { + return new Class[] {ExceptionConst.ARITHMETIC_EXCEPTION}; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LLOAD.java b/src/main/java/haidnor/jvm/bcel/generic/LLOAD.java new file mode 100644 index 0000000..6e1b7af --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LLOAD.java @@ -0,0 +1,52 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LLOAD - Load long from local variable + * + *
+ * Stack ... -> ..., result.word1, result.word2
+ * 
+ */ +public class LLOAD extends LoadInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + LLOAD() { + super(Const.LLOAD, Const.LLOAD_0); + } + + public LLOAD(final int n) { + super(Const.LLOAD, Const.LLOAD_0, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + super.accept(v); + v.visitLLOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LMUL.java b/src/main/java/haidnor/jvm/bcel/generic/LMUL.java new file mode 100644 index 0000000..4b60c17 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LMUL.java @@ -0,0 +1,50 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LMUL - Multiply longs + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 ->
+ * 
+ * + * ..., result.word1, result.word2 + */ +public class LMUL extends ArithmeticInstruction { + + public LMUL() { + super(Const.LMUL); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLMUL(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LNEG.java b/src/main/java/haidnor/jvm/bcel/generic/LNEG.java new file mode 100644 index 0000000..71e33ad --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LNEG.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LNEG - Negate long + * + *
+ * Stack: ..., value.word1, value.word2 -> ..., result.word1, result.word2
+ * 
+ */ +public class LNEG extends ArithmeticInstruction { + + public LNEG() { + super(Const.LNEG); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLNEG(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LOOKUPSWITCH.java b/src/main/java/haidnor/jvm/bcel/generic/LOOKUPSWITCH.java new file mode 100644 index 0000000..47c8c87 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LOOKUPSWITCH.java @@ -0,0 +1,97 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * LOOKUPSWITCH - Switch with unordered set of values + * + * @see SWITCH + */ +public class LOOKUPSWITCH extends Select { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + LOOKUPSWITCH() { + } + + public LOOKUPSWITCH(final int[] match, final InstructionHandle[] targets, final InstructionHandle defaultTarget) { + super(Const.LOOKUPSWITCH, match, targets, defaultTarget); + /* alignment remainder assumed 0 here, until dump time. */ + final short length = (short) (9 + getMatchLength() * 8); + super.setLength(length); + setFixedLength(length); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitVariableLengthInstruction(this); + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitSelect(this); + v.visitLOOKUPSWITCH(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + super.dump(out); + final int matchLength = getMatchLength(); + out.writeInt(matchLength); // npairs + for (int i = 0; i < matchLength; i++) { + out.writeInt(super.getMatch(i)); // match-offset pairs + out.writeInt(setIndices(i, getTargetOffset(super.getTarget(i)))); + } + } + + /** + * Read needed data (e.g. index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + super.initFromFile(bytes, wide); // reads padding + final int matchLength = bytes.readInt(); + setMatchLength(matchLength); + final short fixedLength = (short) (9 + matchLength * 8); + setFixedLength(fixedLength); + final short length = (short) (matchLength + super.getPadding()); + super.setLength(length); + super.setMatches(new int[matchLength]); + super.setIndices(new int[matchLength]); + super.setTargets(new InstructionHandle[matchLength]); + for (int i = 0; i < matchLength; i++) { + super.setMatch(i, bytes.readInt()); + super.setIndices(i, bytes.readInt()); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LOR.java b/src/main/java/haidnor/jvm/bcel/generic/LOR.java new file mode 100644 index 0000000..cc62a8c --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LOR.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LOR - Bitwise OR long + * + *
+ * Stack: ..., value1, value2 -> ..., result
+ * 
+ */ +public class LOR extends ArithmeticInstruction { + + public LOR() { + super(Const.LOR); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLOR(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LREM.java b/src/main/java/haidnor/jvm/bcel/generic/LREM.java new file mode 100644 index 0000000..22f164e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LREM.java @@ -0,0 +1,55 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * LREM - Remainder of long + * + *
+ * Stack: ..., value1, value2 -> result
+ * 
+ */ +public class LREM extends ArithmeticInstruction implements ExceptionThrower { + + public LREM() { + super(Const.LREM); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLREM(this); + } + + @Override + public Class[] getExceptions() { + return new Class[] {ExceptionConst.ARITHMETIC_EXCEPTION}; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LRETURN.java b/src/main/java/haidnor/jvm/bcel/generic/LRETURN.java new file mode 100644 index 0000000..38c136e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LRETURN.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LRETURN - Return long from method + * + *
+ * Stack: ..., value.word1, value.word2 -> <empty>
+ * 
+ */ +public class LRETURN extends ReturnInstruction { + + public LRETURN() { + super(Const.LRETURN); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackConsumer(this); + v.visitReturnInstruction(this); + v.visitLRETURN(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LSHL.java b/src/main/java/haidnor/jvm/bcel/generic/LSHL.java new file mode 100644 index 0000000..0a6d003 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LSHL.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LSHL - Arithmetic shift left long + * + *
+ * Stack: ..., value1.word1, value1.word2, value2 -> ..., result.word1, result.word2
+ * 
+ */ +public class LSHL extends ArithmeticInstruction { + + public LSHL() { + super(Const.LSHL); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLSHL(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LSHR.java b/src/main/java/haidnor/jvm/bcel/generic/LSHR.java new file mode 100644 index 0000000..9e67f62 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LSHR.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LSHR - Arithmetic shift right long + * + *
+ * Stack: ..., value1.word1, value1.word2, value2 -> ..., result.word1, result.word2
+ * 
+ */ +public class LSHR extends ArithmeticInstruction { + + public LSHR() { + super(Const.LSHR); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLSHR(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LSTORE.java b/src/main/java/haidnor/jvm/bcel/generic/LSTORE.java new file mode 100644 index 0000000..a1a5c8c --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LSTORE.java @@ -0,0 +1,52 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LSTORE - Store long into local variable + * + *
+ * Stack: ..., value.word1, value.word2 -> ...
+ * 
+ */ +public class LSTORE extends StoreInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + LSTORE() { + super(Const.LSTORE, Const.LSTORE_0); + } + + public LSTORE(final int n) { + super(Const.LSTORE, Const.LSTORE_0, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + super.accept(v); + v.visitLSTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LSUB.java b/src/main/java/haidnor/jvm/bcel/generic/LSUB.java new file mode 100644 index 0000000..e81bcdd --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LSUB.java @@ -0,0 +1,50 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LSUB - Substract longs + * + *
+ * Stack: ..., value1.word1, value1.word2, value2.word1, value2.word2 ->
+ * 
+ * + * ..., result.word1, result.word2 + */ +public class LSUB extends ArithmeticInstruction { + + public LSUB() { + super(Const.LSUB); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLSUB(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LUSHR.java b/src/main/java/haidnor/jvm/bcel/generic/LUSHR.java new file mode 100644 index 0000000..2f3f4f3 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LUSHR.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LUSHR - Logical shift right long + * + *
+ * Stack: ..., value1, value2 -> ..., result
+ * 
+ */ +public class LUSHR extends ArithmeticInstruction { + + public LUSHR() { + super(Const.LUSHR); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLUSHR(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LXOR.java b/src/main/java/haidnor/jvm/bcel/generic/LXOR.java new file mode 100644 index 0000000..5d04204 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LXOR.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * LXOR - Bitwise XOR long + * + *
+ * Stack: ..., value1, value2 -> ..., result
+ * 
+ */ +public class LXOR extends ArithmeticInstruction { + + public LXOR() { + super(Const.LXOR); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitTypedInstruction(this); + v.visitStackProducer(this); + v.visitStackConsumer(this); + v.visitArithmeticInstruction(this); + v.visitLXOR(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LineNumberGen.java b/src/main/java/haidnor/jvm/bcel/generic/LineNumberGen.java new file mode 100644 index 0000000..4376c5d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LineNumberGen.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.LineNumber; + +import java.util.Objects; + +/** + * This class represents a line number within a method, i.e., give an instruction a line number corresponding to the + * source code line. + * + * @see LineNumber + * @see MethodGen + */ +public class LineNumberGen implements InstructionTargeter, Cloneable { + + static final LineNumberGen[] EMPTY_ARRAY = {}; + + private InstructionHandle ih; + private int srcLine; + + /** + * Create a line number. + * + * @param ih instruction handle to reference + */ + public LineNumberGen(final InstructionHandle ih, final int srcLine) { + setInstruction(ih); + setSourceLine(srcLine); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (final CloneNotSupportedException e) { + throw new Error("Clone Not Supported"); // never happens + } + } + + /** + * @return true, if ih is target of this line number + */ + @Override + public boolean containsTarget(final InstructionHandle ih) { + return this.ih == ih; + } + + public InstructionHandle getInstruction() { + return ih; + } + + /** + * Get LineNumber attribute. + * + * This relies on that the instruction list has already been dumped to byte code or that the 'setPositions' methods + * has been called for the instruction list. + */ + public LineNumber getLineNumber() { + return new LineNumber(ih.getPosition(), srcLine); + } + + public int getSourceLine() { + return srcLine; + } + + public void setInstruction(final InstructionHandle instructionHandle) { // TODO could be package-protected? + Objects.requireNonNull(instructionHandle, "instructionHandle"); + BranchInstruction.notifyTarget(this.ih, instructionHandle, this); + this.ih = instructionHandle; + } + + public void setSourceLine(final int srcLine) { // TODO could be package-protected? + this.srcLine = srcLine; + } + + /** + * @param oldIh old target + * @param newIh new target + */ + @Override + public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) { + if (oldIh != ih) { + throw new ClassGenException("Not targeting " + oldIh + ", but " + ih + "}"); + } + setInstruction(newIh); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LoadClass.java b/src/main/java/haidnor/jvm/bcel/generic/LoadClass.java new file mode 100644 index 0000000..e54f449 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LoadClass.java @@ -0,0 +1,43 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denotes that an instruction may start the process of loading and resolving the referenced class in the Virtual Machine. + */ +public interface LoadClass { + + /** + * Returns the {@link ObjectType} of the referenced class or interface that may be loaded and resolved. + * + * @param cpg A ConstantPoolGen + * @return object type that may be loaded or null if a primitive is referenced + */ + ObjectType getLoadClassType(ConstantPoolGen cpg); + + /** + * Returns the type associated with this instruction. LoadClass instances are always typed, but this type does not always refer to the type of the class or + * interface that it possibly forces to load. For example, {@link GETFIELD} would return the type of the field and not the type of the class where the field + * is defined. If no class is forced to be loaded, {@code null} is returned. An example for this is an {@link NEWARRAY} instruction that creates an + * {@code int[][]}. + * + * @param cpg A ConstantPoolGen + * @return the type associated with this instruction. + * @see #getLoadClassType(ConstantPoolGen) + */ + Type getType(ConstantPoolGen cpg); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LoadInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/LoadInstruction.java new file mode 100644 index 0000000..f79c968 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LoadInstruction.java @@ -0,0 +1,55 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denotes an unparameterized instruction to load a value from a local variable, e.g. ILOAD. + */ +public abstract class LoadInstruction extends LocalVariableInstruction implements PushInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. tag and length are defined in + * readInstruction and initFromFile, respectively. + */ + LoadInstruction(final short canonTag, final short cTag) { + super(canonTag, cTag); + } + + /** + * @param opcode Instruction opcode + * @param cTag Instruction number for compact version, ALOAD_0, e.g. + * @param n local variable index (unsigned short) + */ + protected LoadInstruction(final short opcode, final short cTag, final int n) { + super(opcode, cTag, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitPushInstruction(this); + v.visitTypedInstruction(this); + v.visitLocalVariableInstruction(this); + v.visitLoadInstruction(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LocalVariableGen.java b/src/main/java/haidnor/jvm/bcel/generic/LocalVariableGen.java new file mode 100644 index 0000000..0c6bcb1 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LocalVariableGen.java @@ -0,0 +1,232 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.LocalVariable; + +/** + * Represents a local variable within a method. It contains its scope, name and type. The generated LocalVariable object + * can be obtained with getLocalVariable which needs the instruction list and the constant pool as parameters. + * + * @see LocalVariable + * @see MethodGen + */ +public class LocalVariableGen implements InstructionTargeter, NamedAndTyped, Cloneable { + + private int index; + private String name; + private Type type; + private InstructionHandle start; + private InstructionHandle end; + private int origIndex; // never changes; used to match up with LocalVariableTypeTable entries + private boolean liveToEnd; + + /** + * Generate a local variable that with index 'index'. Note that double and long variables need two indexs. Index indices + * have to be provided by the user. + * + * @param index index of local variable + * @param name its name + * @param type its type + * @param start from where the instruction is valid (null means from the start) + * @param end until where the instruction is valid (null means to the end) + */ + public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end) { + if (index < 0 || index > Const.MAX_SHORT) { + throw new ClassGenException("Invalid index: " + index); + } + this.name = name; + this.type = type; + this.index = index; + setStart(start); + setEnd(end); + this.origIndex = index; + this.liveToEnd = end == null; + } + + /** + * Generates a local variable that with index 'index'. Note that double and long variables need two indexs. Index + * indices have to be provided by the user. + * + * @param index index of local variable + * @param name its name + * @param type its type + * @param start from where the instruction is valid (null means from the start) + * @param end until where the instruction is valid (null means to the end) + * @param origIndex index of local variable prior to any changes to index + */ + public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end, + final int origIndex) { + this(index, name, type, start, end); + this.origIndex = origIndex; + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (final CloneNotSupportedException e) { + throw new Error("Clone Not Supported"); // never happens + } + } + + /** + * @return true, if ih is target of this variable + */ + @Override + public boolean containsTarget(final InstructionHandle ih) { + return start == ih || end == ih; + } + + /** + * Clear the references from and to this variable when it's removed. + */ + void dispose() { + setStart(null); + setEnd(null); + } + + /** + * We consider to local variables to be equal, if the use the same index and are valid in the same range. + */ + @Override + public boolean equals(final Object o) { + if (!(o instanceof LocalVariableGen)) { + return false; + } + final LocalVariableGen l = (LocalVariableGen) o; + return l.index == index && l.start == start && l.end == end; + } + + public InstructionHandle getEnd() { + return end; + } + + public int getIndex() { + return index; + } + + public boolean getLiveToEnd() { + return liveToEnd; + } + + /** + * Gets LocalVariable object. + * + * This relies on that the instruction list has already been dumped to byte code or that the 'setPositions' methods + * has been called for the instruction list. + * + * Note that due to the conversion from byte code offset to InstructionHandle, it is impossible to tell the difference + * between a live range that ends BEFORE the last insturction of the method or a live range that ends AFTER the last + * instruction of the method. Hence the liveToEnd flag to differentiate between these two cases. + * + * @param cp constant pool + */ + public LocalVariable getLocalVariable(final ConstantPoolGen cp) { + int startPc = 0; + int length = 0; + if (start != null && end != null) { + startPc = start.getPosition(); + length = end.getPosition() - startPc; + if (end.getNext() == null && liveToEnd) { + length += end.getInstruction().getLength(); + } + } + final int nameIndex = cp.addUtf8(name); + final int signatureIndex = cp.addUtf8(type.getSignature()); + return new LocalVariable(startPc, length, nameIndex, signatureIndex, index, cp.getConstantPool(), origIndex); + } + + @Override + public String getName() { + return name; + } + + public int getOrigIndex() { + return origIndex; + } + + public InstructionHandle getStart() { + return start; + } + + @Override + public Type getType() { + return type; + } + + @Override + public int hashCode() { + // If the user changes the name or type, problems with the targeter hashmap will occur. + // Note: index cannot be part of hash as it may be changed by the user. + return name.hashCode() ^ type.hashCode(); + } + + public void setEnd(final InstructionHandle end) { // TODO could be package-protected? + BranchInstruction.notifyTarget(this.end, end, this); + this.end = end; + } + + public void setIndex(final int index) { + this.index = index; + } + + public void setLiveToEnd(final boolean liveToEnd) { + this.liveToEnd = liveToEnd; + } + + @Override + public void setName(final String name) { + this.name = name; + } + + public void setStart(final InstructionHandle start) { // TODO could be package-protected? + BranchInstruction.notifyTarget(this.start, start, this); + this.start = start; + } + + @Override + public void setType(final Type type) { + this.type = type; + } + + @Override + public String toString() { + return "LocalVariableGen(" + name + ", " + type + ", " + start + ", " + end + ")"; + } + + /** + * @param oldIh old target, either start or end + * @param newIh new target + */ + @Override + public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) { + boolean targeted = false; + if (start == oldIh) { + targeted = true; + setStart(newIh); + } + if (end == oldIh) { + targeted = true; + setEnd(newIh); + } + if (!targeted) { + throw new ClassGenException("Not targeting " + oldIh + ", but {" + start + ", " + end + "}"); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/LocalVariableInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/LocalVariableInstruction.java new file mode 100644 index 0000000..cde6f91 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/LocalVariableInstruction.java @@ -0,0 +1,215 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Abstract super class for instructions dealing with local variables. + */ +public abstract class LocalVariableInstruction extends Instruction implements TypedInstruction, IndexedInstruction { + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected int n = -1; // index of referenced variable + + private short cTag = -1; // compact version, such as ILOAD_0 + private short canonTag = -1; // canonical tag such as ILOAD + + /** + * Empty constructor needed for Instruction.readInstruction. Also used by IINC()! + */ + LocalVariableInstruction() { + } + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. tag and length are defined in + * readInstruction and initFromFile, respectively. + */ + LocalVariableInstruction(final short canonTag, final short cTag) { + this.canonTag = canonTag; + this.cTag = cTag; + } + + /** + * @param opcode Instruction opcode + * @param cTag Instruction number for compact version, ALOAD_0, e.g. + * @param n local variable index (unsigned short) + */ + protected LocalVariableInstruction(final short opcode, final short cTag, final int n) { + super(opcode, (short) 2); + this.cTag = cTag; + canonTag = opcode; + setIndex(n); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + if (wide()) { + out.writeByte(Const.WIDE); + } + out.writeByte(super.getOpcode()); + if (super.getLength() > 1) { // Otherwise ILOAD_n, instruction, e.g. + if (wide()) { + out.writeShort(n); + } else { + out.writeByte(n); + } + } + } + + /** + * @return canonical tag for instruction, e.g., ALOAD for ALOAD_0 + */ + public short getCanonicalTag() { + return canonTag; + } + + /** + * @return local variable index (n) referred by this instruction. + */ + @Override + public final int getIndex() { + return n; + } + + /** + * Returns the type associated with the instruction - in case of ALOAD or ASTORE Type.OBJECT is returned. This is just a + * bit incorrect, because ALOAD and ASTORE may work on every ReferenceType (including Type.NULL) and ASTORE may even + * work on a ReturnaddressType . + * + * @return type associated with the instruction + */ + @Override + public Type getType(final ConstantPoolGen cp) { + switch (canonTag) { + case Const.ILOAD: + case Const.ISTORE: + return Type.INT; + case Const.LLOAD: + case Const.LSTORE: + return Type.LONG; + case Const.DLOAD: + case Const.DSTORE: + return Type.DOUBLE; + case Const.FLOAD: + case Const.FSTORE: + return Type.FLOAT; + case Const.ALOAD: + case Const.ASTORE: + return Type.OBJECT; + default: + throw new ClassGenException("Unknown case in switch" + canonTag); + } + } + + /** + * Read needed data (e.g. index) from file. + * + *
+     * (ILOAD <= tag <= ALOAD_3) || (ISTORE <= tag <= ASTORE_3)
+     * 
+ */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + if (wide) { + n = bytes.readUnsignedShort(); + super.setLength(4); + } else { + final short opcode = super.getOpcode(); + if (opcode >= Const.ILOAD && opcode <= Const.ALOAD || opcode >= Const.ISTORE && opcode <= Const.ASTORE) { + n = bytes.readUnsignedByte(); + super.setLength(2); + } else { + if (opcode <= Const.ALOAD_3) { // compact load instruction such as ILOAD_2 + n = (opcode - Const.ILOAD_0) % 4; + } else { // Assert ISTORE_0 <= tag <= ASTORE_3 + n = (opcode - Const.ISTORE_0) % 4; + } + super.setLength(1); + } + } + } + + /** + * Set the local variable index. also updates opcode and length TODO Why? + * + * @see #setIndexOnly(int) + */ + @Override + public void setIndex(final int n) { // TODO could be package-protected? + if (n < 0 || n > Const.MAX_SHORT) { + throw new ClassGenException("Illegal value: " + n); + } + this.n = n; + // Cannot be < 0 as this is checked above + if (n <= 3) { // Use more compact instruction xLOAD_n + super.setOpcode((short) (cTag + n)); + super.setLength(1); + } else { + super.setOpcode(canonTag); + if (wide()) { + super.setLength(4); + } else { + super.setLength(2); + } + } + } + + /** + * Sets the index of the referenced variable (n) only + * + * @since 6.0 + * @see #setIndex(int) + */ + final void setIndexOnly(final int n) { + this.n = n; + } + + /** + * Long output format: + * + * <name of opcode> "["<opcode number>"]" "("<length of instruction>")" "<"< local variable + * index>">" + * + * @param verbose long/short format switch + * @return mnemonic for instruction + */ + @Override + public String toString(final boolean verbose) { + final short opcode = super.getOpcode(); + if (opcode >= Const.ILOAD_0 && opcode <= Const.ALOAD_3 || opcode >= Const.ISTORE_0 && opcode <= Const.ASTORE_3) { + return super.toString(verbose); + } + return super.toString(verbose) + " " + n; + } + + private boolean wide() { + return n > Const.MAX_BYTE; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/MONITORENTER.java b/src/main/java/haidnor/jvm/bcel/generic/MONITORENTER.java new file mode 100644 index 0000000..6c801f0 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/MONITORENTER.java @@ -0,0 +1,52 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * MONITORENTER - Enter monitor for object + * + *
+ * Stack: ..., objectref -> ...
+ * 
+ */ +public class MONITORENTER extends Instruction implements ExceptionThrower, StackConsumer { + + public MONITORENTER() { + super(Const.MONITORENTER, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitStackConsumer(this); + v.visitMONITORENTER(this); + } + + @Override + public Class[] getExceptions() { + return new Class[] {ExceptionConst.NULL_POINTER_EXCEPTION}; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/MONITOREXIT.java b/src/main/java/haidnor/jvm/bcel/generic/MONITOREXIT.java new file mode 100644 index 0000000..543ca18 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/MONITOREXIT.java @@ -0,0 +1,52 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * MONITOREXIT - Exit monitor for object + * + *
+ * Stack: ..., objectref -> ...
+ * 
+ */ +public class MONITOREXIT extends Instruction implements ExceptionThrower, StackConsumer { + + public MONITOREXIT() { + super(Const.MONITOREXIT, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitStackConsumer(this); + v.visitMONITOREXIT(this); + } + + @Override + public Class[] getExceptions() { + return new Class[] {ExceptionConst.NULL_POINTER_EXCEPTION}; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/MULTIANEWARRAY.java b/src/main/java/haidnor/jvm/bcel/generic/MULTIANEWARRAY.java new file mode 100644 index 0000000..9cb0658 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/MULTIANEWARRAY.java @@ -0,0 +1,138 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; +import haidnor.jvm.bcel.classfile.ConstantPool; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * MULTIANEWARRAY - Create new mutidimensional array of references + * + *
+ * Stack: ..., count1, [count2, ...] -> ..., arrayref
+ * 
+ */ +public class MULTIANEWARRAY extends CPInstruction implements LoadClass, AllocationInstruction, ExceptionThrower { + + private short dimensions; + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + MULTIANEWARRAY() { + } + + public MULTIANEWARRAY(final int index, final short dimensions) { + super(Const.MULTIANEWARRAY, index); + if (dimensions < 1) { + throw new ClassGenException("Invalid dimensions value: " + dimensions); + } + this.dimensions = dimensions; + super.setLength(4); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitLoadClass(this); + v.visitAllocationInstruction(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitCPInstruction(this); + v.visitMULTIANEWARRAY(this); + } + + /** + * Also works for instructions whose stack effect depends on the constant pool entry they reference. + * + * @return Number of words consumed from stack by this instruction + */ + @Override + public int consumeStack(final ConstantPoolGen cpg) { + return dimensions; + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(super.getOpcode()); + out.writeShort(super.getIndex()); + out.writeByte(dimensions); + } + + /** + * @return number of dimensions to be created + */ + public final short getDimensions() { + return dimensions; + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_CLASS_AND_INTERFACE_RESOLUTION, ExceptionConst.ILLEGAL_ACCESS_ERROR, + ExceptionConst.NEGATIVE_ARRAY_SIZE_EXCEPTION); + } + + @Override + public ObjectType getLoadClassType(final ConstantPoolGen cpg) { + Type t = getType(cpg); + if (t instanceof ArrayType) { + t = ((ArrayType) t).getBasicType(); + } + return t instanceof ObjectType ? (ObjectType) t : null; + } + + /** + * Read needed data (i.e., no. dimension) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + super.initFromFile(bytes, wide); + dimensions = bytes.readByte(); + super.setLength(4); + } + + /** + * @return mnemonic for instruction + */ + @Override + public String toString(final boolean verbose) { + return super.toString(verbose) + " " + super.getIndex() + " " + dimensions; + } + + /** + * @return mnemonic for instruction with symbolic references resolved + */ + @Override + public String toString(final ConstantPool cp) { + return super.toString(cp) + " " + dimensions; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/MethodGen.java b/src/main/java/haidnor/jvm/bcel/generic/MethodGen.java new file mode 100644 index 0000000..20a278a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/MethodGen.java @@ -0,0 +1,1152 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.*; +import haidnor.jvm.bcel.util.BCELComparator; +import org.apache.commons.lang3.ArrayUtils; + +import java.util.*; + +/** + * Template class for building up a method. This is done by defining exception handlers, adding thrown exceptions, local + * variables and attributes, whereas the 'LocalVariableTable' and 'LineNumberTable' attributes will be set automatically + * for the code. Use stripAttributes() if you don't like this. + * + * While generating code it may be necessary to insert NOP operations. You can use the 'removeNOPs' method to get rid + * off them. The resulting method object can be obtained via the 'getMethod()' method. + * + * @see InstructionList + * @see JavaMethod + */ +public class MethodGen extends FieldGenOrMethodGen { + + static final class BranchStack { + + private final Stack branchTargets = new Stack<>(); + private final Hashtable visitedTargets = new Hashtable<>(); + + public BranchTarget pop() { + if (!branchTargets.empty()) { + return branchTargets.pop(); + } + return null; + } + + public void push(final InstructionHandle target, final int stackDepth) { + if (visited(target)) { + return; + } + branchTargets.push(visit(target, stackDepth)); + } + + private BranchTarget visit(final InstructionHandle target, final int stackDepth) { + final BranchTarget bt = new BranchTarget(target, stackDepth); + visitedTargets.put(target, bt); + return bt; + } + + private boolean visited(final InstructionHandle target) { + return visitedTargets.get(target) != null; + } + } + + static final class BranchTarget { + + final InstructionHandle target; + final int stackDepth; + + BranchTarget(final InstructionHandle target, final int stackDepth) { + this.target = target; + this.stackDepth = stackDepth; + } + } + + private static BCELComparator bcelComparator = new BCELComparator() { + + @Override + public boolean equals(final Object o1, final Object o2) { + final FieldGenOrMethodGen THIS = (FieldGenOrMethodGen) o1; + final FieldGenOrMethodGen THAT = (FieldGenOrMethodGen) o2; + return Objects.equals(THIS.getName(), THAT.getName()) && Objects.equals(THIS.getSignature(), THAT.getSignature()); + } + + @Override + public int hashCode(final Object o) { + final FieldGenOrMethodGen THIS = (FieldGenOrMethodGen) o; + return THIS.getSignature().hashCode() ^ THIS.getName().hashCode(); + } + }; + + private static byte[] getByteCodes(final JavaMethod method) { + final Code code = method.getCode(); + if (code == null) { + throw new IllegalStateException(String.format("The method '%s' has no code.", method)); + } + return code.getCode(); + } + + /** + * @return Comparison strategy object + */ + public static BCELComparator getComparator() { + return bcelComparator; + } + + /** + * Computes stack usage of an instruction list by performing control flow analysis. + * + * @return maximum stack depth used by method + */ + public static int getMaxStack(final ConstantPoolGen cp, final InstructionList il, final CodeExceptionGen[] et) { + final BranchStack branchTargets = new BranchStack(); + /* + * Initially, populate the branch stack with the exception handlers, because these aren't (necessarily) branched to + * explicitly. in each case, the stack will have depth 1, containing the exception object. + */ + for (final CodeExceptionGen element : et) { + final InstructionHandle handlerPc = element.getHandlerPC(); + if (handlerPc != null) { + branchTargets.push(handlerPc, 1); + } + } + int stackDepth = 0; + int maxStackDepth = 0; + InstructionHandle ih = il.getStart(); + while (ih != null) { + final Instruction instruction = ih.getInstruction(); + final short opcode = instruction.getOpcode(); + final int delta = instruction.produceStack(cp) - instruction.consumeStack(cp); + stackDepth += delta; + if (stackDepth > maxStackDepth) { + maxStackDepth = stackDepth; + } + // choose the next instruction based on whether current is a branch. + if (instruction instanceof BranchInstruction) { + final BranchInstruction branch = (BranchInstruction) instruction; + if (instruction instanceof Select) { + // explore all of the select's targets. the default target is handled below. + final Select select = (Select) branch; + final InstructionHandle[] targets = select.getTargets(); + for (final InstructionHandle target : targets) { + branchTargets.push(target, stackDepth); + } + // nothing to fall through to. + ih = null; + } else if (!(branch instanceof IfInstruction)) { + // if an instruction that comes back to following PC, + // push next instruction, with stack depth reduced by 1. + if (opcode == Const.JSR || opcode == Const.JSR_W) { + branchTargets.push(ih.getNext(), stackDepth - 1); + } + ih = null; + } + // for all branches, the target of the branch is pushed on the branch stack. + // conditional branches have a fall through case, selects don't, and + // jsr/jsr_w return to the next instruction. + branchTargets.push(branch.getTarget(), stackDepth); + } else // check for instructions that terminate the method. + if (opcode == Const.ATHROW || opcode == Const.RET || opcode >= Const.IRETURN && opcode <= Const.RETURN) { + ih = null; + } + // normal case, go to the next instruction. + if (ih != null) { + ih = ih.getNext(); + } + // if we have no more instructions, see if there are any deferred branches to explore. + if (ih == null) { + final BranchTarget bt = branchTargets.pop(); + if (bt != null) { + ih = bt.target; + stackDepth = bt.stackDepth; + } + } + } + return maxStackDepth; + } + + /** + * @param comparator Comparison strategy object + */ + public static void setComparator(final BCELComparator comparator) { + bcelComparator = comparator; + } + + private String className; + private Type[] argTypes; + private String[] argNames; + private int maxLocals; + private int maxStack; + private InstructionList il; + + private boolean stripAttributes; + private LocalVariableTypeTable localVariableTypeTable; + private final List variableList = new ArrayList<>(); + + private final List lineNumberList = new ArrayList<>(); + + private final List exceptionList = new ArrayList<>(); + + private final List throwsList = new ArrayList<>(); + + private final List codeAttrsList = new ArrayList<>(); + + private List[] paramAnnotations; // Array of lists containing AnnotationGen objects + + private boolean hasParameterAnnotations; + + private boolean haveUnpackedParameterAnnotations; + + private List observers; + + /** + * Declare method. If the method is non-static the constructor automatically declares a local variable '$this' in slot + * 0. The actual code is contained in the 'il' parameter, which may further manipulated by the user. But they must take + * care not to remove any instruction (handles) that are still referenced from this object. + * + * For example one may not add a local variable and later remove the instructions it refers to without causing havoc. It + * is safe however if you remove that local variable, too. + * + * @param accessFlags access qualifiers + * @param returnType method type + * @param argTypes argument types + * @param argNames argument names (if this is null, default names will be provided for them) + * @param methodName name of method + * @param className class name containing this method (may be null, if you don't care) + * @param il instruction list associated with this method, may be null only for abstract or native methods + * @param cp constant pool + */ + public MethodGen(final int accessFlags, final Type returnType, final Type[] argTypes, String[] argNames, final String methodName, final String className, + final InstructionList il, final ConstantPoolGen cp) { + super(accessFlags); + setType(returnType); + setArgumentTypes(argTypes); + setArgumentNames(argNames); + setName(methodName); + setClassName(className); + setInstructionList(il); + setConstantPool(cp); + final boolean abstract_ = isAbstract() || isNative(); + InstructionHandle start = null; + final InstructionHandle end = null; + if (!abstract_) { + start = il.getStart(); + // end == null => live to end of method + /* + * Add local variables, namely the implicit 'this' and the arguments + */ + if (!isStatic() && className != null) { // Instance method -> 'this' is local var 0 + addLocalVariable("this", ObjectType.getInstance(className), start, end); + } + } + if (argTypes != null) { + final int size = argTypes.length; + for (final Type argType : argTypes) { + if (Type.VOID == argType) { + throw new ClassGenException("'void' is an illegal argument type for a method"); + } + } + if (argNames != null) { // Names for variables provided? + if (size != argNames.length) { + throw new ClassGenException("Mismatch in argument array lengths: " + size + " vs. " + argNames.length); + } + } else { // Give them dummy names + argNames = new String[size]; + for (int i = 0; i < size; i++) { + argNames[i] = "arg" + i; + } + setArgumentNames(argNames); + } + if (!abstract_) { + for (int i = 0; i < size; i++) { + addLocalVariable(argNames[i], argTypes[i], start, end); + } + } + } + } + + /** + * Instantiate from existing method. + * + * @param method method + * @param className class name containing this method + * @param cp constant pool + */ + public MethodGen(final JavaMethod method, final String className, final ConstantPoolGen cp) { + this(method.getAccessFlags(), Type.getReturnType(method.getSignature()), Type.getArgumentTypes(method.getSignature()), + null /* may be overridden anyway */ + , method.getName(), className, + (method.getAccessFlags() & (Const.ACC_ABSTRACT | Const.ACC_NATIVE)) == 0 ? new InstructionList(getByteCodes(method)) : null, cp); + final Attribute[] attributes = method.getAttributes(); + for (final Attribute attribute : attributes) { + Attribute a = attribute; + if (a instanceof Code) { + final Code c = (Code) a; + setMaxStack(c.getMaxStack()); + setMaxLocals(c.getMaxLocals()); + final CodeException[] ces = c.getExceptionTable(); + if (ces != null) { + for (final CodeException ce : ces) { + final int type = ce.getCatchType(); + ObjectType cType = null; + if (type > 0) { + final String cen = method.getConstantPool().getConstantString(type, Const.CONSTANT_Class); + cType = ObjectType.getInstance(cen); + } + final int endPc = ce.getEndPC(); + final int length = getByteCodes(method).length; + InstructionHandle end; + if (length == endPc) { // May happen, because end_pc is exclusive + end = il.getEnd(); + } else { + end = il.findHandle(endPc); + end = end.getPrev(); // Make it inclusive + } + addExceptionHandler(il.findHandle(ce.getStartPC()), end, il.findHandle(ce.getHandlerPC()), cType); + } + } + final Attribute[] cAttributes = c.getAttributes(); + for (final Attribute cAttribute : cAttributes) { + a = cAttribute; + if (a instanceof LineNumberTable) { + ((LineNumberTable) a).forEach(l -> { + final InstructionHandle ih = il.findHandle(l.getStartPC()); + if (ih != null) { + addLineNumber(ih, l.getLineNumber()); + } + }); + } else if (a instanceof LocalVariableTable) { + updateLocalVariableTable((LocalVariableTable) a); + } else if (a instanceof LocalVariableTypeTable) { + this.localVariableTypeTable = (LocalVariableTypeTable) a.copy(cp.getConstantPool()); + } else { + addCodeAttribute(a); + } + } + } else if (a instanceof ExceptionTable) { + Collections.addAll(throwsList, ((ExceptionTable) a).getExceptionNames()); + } else if (a instanceof Annotations) { + final Annotations runtimeAnnotations = (Annotations) a; + runtimeAnnotations.forEach(element -> addAnnotationEntry(new AnnotationEntryGen(element, cp, false))); + } else { + addAttribute(a); + } + } + } + + /** + * @since 6.0 + */ + public void addAnnotationsAsAttribute(final ConstantPoolGen cp) { + addAll(AnnotationEntryGen.getAnnotationAttributes(cp, super.getAnnotationEntries())); + } + + /** + * Add an attribute to the code. Currently, the JVM knows about the LineNumberTable, LocalVariableTable and StackMap + * attributes, where the former two will be generated automatically and the latter is used for the MIDP only. Other + * attributes will be ignored by the JVM but do no harm. + * + * @param a attribute to be added + */ + public void addCodeAttribute(final Attribute a) { + codeAttrsList.add(a); + } + + /** + * Add an exception possibly thrown by this method. + * + * @param className (fully qualified) name of exception + */ + public void addException(final String className) { + throwsList.add(className); + } + + /** + * Add an exception handler, i.e., specify region where a handler is active and an instruction where the actual handling + * is done. + * + * @param startPc Start of region (inclusive) + * @param endPc End of region (inclusive) + * @param handlerPc Where handling is done + * @param catchType class type of handled exception or null if any exception is handled + * @return new exception handler object + */ + public CodeExceptionGen addExceptionHandler(final InstructionHandle startPc, final InstructionHandle endPc, final InstructionHandle handlerPc, + final ObjectType catchType) { + if (startPc == null || endPc == null || handlerPc == null) { + throw new ClassGenException("Exception handler target is null instruction"); + } + final CodeExceptionGen c = new CodeExceptionGen(startPc, endPc, handlerPc, catchType); + exceptionList.add(c); + return c; + } + + /** + * Give an instruction a line number corresponding to the source code line. + * + * @param ih instruction to tag + * @return new line number object + * @see LineNumber + */ + public LineNumberGen addLineNumber(final InstructionHandle ih, final int srcLine) { + final LineNumberGen l = new LineNumberGen(ih, srcLine); + lineNumberList.add(l); + return l; + } + + /** + * Adds a local variable to this method and assigns an index automatically. + * + * @param name variable name + * @param type variable type + * @param start from where the variable is valid, if this is null, it is valid from the start + * @param end until where the variable is valid, if this is null, it is valid to the end + * @return new local variable object + * @see LocalVariable + */ + public LocalVariableGen addLocalVariable(final String name, final Type type, final InstructionHandle start, final InstructionHandle end) { + return addLocalVariable(name, type, maxLocals, start, end); + } + + /** + * Adds a local variable to this method. + * + * @param name variable name + * @param type variable type + * @param slot the index of the local variable, if type is long or double, the next available index is slot+2 + * @param start from where the variable is valid + * @param end until where the variable is valid + * @return new local variable object + * @see LocalVariable + */ + public LocalVariableGen addLocalVariable(final String name, final Type type, final int slot, final InstructionHandle start, final InstructionHandle end) { + return addLocalVariable(name, type, slot, start, end, slot); + } + + /** + * Adds a local variable to this method. + * + * @param name variable name + * @param type variable type + * @param slot the index of the local variable, if type is long or double, the next available index is slot+2 + * @param start from where the variable is valid + * @param end until where the variable is valid + * @param origIndex the index of the local variable prior to any modifications + * @return new local variable object + * @see LocalVariable + */ + public LocalVariableGen addLocalVariable(final String name, final Type type, final int slot, final InstructionHandle start, final InstructionHandle end, + final int origIndex) { + final byte t = type.getType(); + if (t != Const.T_ADDRESS) { + final int add = type.getSize(); + if (slot + add > maxLocals) { + maxLocals = slot + add; + } + final LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end, origIndex); + int i; + if ((i = variableList.indexOf(l)) >= 0) { + variableList.set(i, l); + } else { + variableList.add(l); + } + return l; + } + throw new IllegalArgumentException("Can not use " + type + " as type for local variable"); + } + + /** + * Add observer for this object. + */ + public void addObserver(final MethodObserver o) { + if (observers == null) { + observers = new ArrayList<>(); + } + observers.add(o); + } + + public void addParameterAnnotation(final int parameterIndex, final AnnotationEntryGen annotation) { + ensureExistingParameterAnnotationsUnpacked(); + if (!hasParameterAnnotations) { + @SuppressWarnings("unchecked") // OK + final List[] parmList = new List[argTypes.length]; + paramAnnotations = parmList; + hasParameterAnnotations = true; + } + final List existingAnnotations = paramAnnotations[parameterIndex]; + if (existingAnnotations != null) { + existingAnnotations.add(annotation); + } else { + final List l = new ArrayList<>(); + l.add(annotation); + paramAnnotations[parameterIndex] = l; + } + } + + /** + * @since 6.0 + */ + public void addParameterAnnotationsAsAttribute(final ConstantPoolGen cp) { + if (!hasParameterAnnotations) { + return; + } + final Attribute[] attrs = AnnotationEntryGen.getParameterAnnotationAttributes(cp, paramAnnotations); + if (attrs != null) { + addAll(attrs); + } + } + + private Attribute[] addRuntimeAnnotationsAsAttribute(final ConstantPoolGen cp) { + final Attribute[] attrs = AnnotationEntryGen.getAnnotationAttributes(cp, super.getAnnotationEntries()); + addAll(attrs); + return attrs; + } + + private Attribute[] addRuntimeParameterAnnotationsAsAttribute(final ConstantPoolGen cp) { + if (!hasParameterAnnotations) { + return Attribute.EMPTY_ARRAY; + } + final Attribute[] attrs = AnnotationEntryGen.getParameterAnnotationAttributes(cp, paramAnnotations); + addAll(attrs); + return attrs; + } + + private void adjustLocalVariableTypeTable(final LocalVariableTable lvt) { + final LocalVariable[] lv = lvt.getLocalVariableTable(); + for (final LocalVariable element : localVariableTypeTable.getLocalVariableTypeTable()) { + for (final LocalVariable l : lv) { + if (element.getName().equals(l.getName()) && element.getIndex() == l.getOrigIndex()) { + element.setLength(l.getLength()); + element.setStartPC(l.getStartPC()); + element.setIndex(l.getIndex()); + break; + } + } + } + } + + /** + * @return deep copy of this method + */ + public MethodGen copy(final String className, final ConstantPoolGen cp) { + final JavaMethod m = ((MethodGen) clone()).getMethod(); + final MethodGen mg = new MethodGen(m, className, super.getConstantPool()); + if (super.getConstantPool() != cp) { + mg.setConstantPool(cp); + mg.getInstructionList().replaceConstantPool(super.getConstantPool(), cp); + } + return mg; + } + + /** + * Goes through the attributes on the method and identifies any that are RuntimeParameterAnnotations, extracting their + * contents and storing them as parameter annotations. There are two kinds of parameter annotation - visible and + * invisible. Once they have been unpacked, these attributes are deleted. (The annotations will be rebuilt as attributes + * when someone builds a Method object out of this MethodGen object). + */ + private void ensureExistingParameterAnnotationsUnpacked() { + if (haveUnpackedParameterAnnotations) { + return; + } + // Find attributes that contain parameter annotation data + final Attribute[] attrs = getAttributes(); + ParameterAnnotations paramAnnVisAttr = null; + ParameterAnnotations paramAnnInvisAttr = null; + for (final Attribute attribute : attrs) { + if (attribute instanceof ParameterAnnotations) { + // Initialize paramAnnotations + if (!hasParameterAnnotations) { + @SuppressWarnings("unchecked") // OK + final List[] parmList = new List[argTypes.length]; + paramAnnotations = parmList; + Arrays.setAll(paramAnnotations, i -> new ArrayList<>()); + } + hasParameterAnnotations = true; + final ParameterAnnotations rpa = (ParameterAnnotations) attribute; + if (rpa instanceof RuntimeVisibleParameterAnnotations) { + paramAnnVisAttr = rpa; + } else { + paramAnnInvisAttr = rpa; + } + final ParameterAnnotationEntry[] parameterAnnotationEntries = rpa.getParameterAnnotationEntries(); + for (int j = 0; j < parameterAnnotationEntries.length; j++) { + // This returns Annotation[] ... + final ParameterAnnotationEntry immutableArray = rpa.getParameterAnnotationEntries()[j]; + // ... which needs transforming into an AnnotationGen[] ... + final List mutable = makeMutableVersion(immutableArray.getAnnotationEntries()); + // ... then add these to any we already know about + paramAnnotations[j].addAll(mutable); + } + } + } + if (paramAnnVisAttr != null) { + removeAttribute(paramAnnVisAttr); + } + if (paramAnnInvisAttr != null) { + removeAttribute(paramAnnInvisAttr); + } + haveUnpackedParameterAnnotations = true; + } + + /** + * Return value as defined by given BCELComparator strategy. By default two MethodGen objects are said to be equal when + * their names and signatures are equal. + * + * @see Object#equals(Object) + */ + @Override + public boolean equals(final Object obj) { + return bcelComparator.equals(this, obj); + } + + // J5TODO: Should paramAnnotations be an array of arrays? Rather than an array of lists, this + // is more likely to suggest to the caller it is readonly (which a List does not). + /** + * Return a list of AnnotationGen objects representing parameter annotations + * + * @since 6.0 + */ + public List getAnnotationsOnParameter(final int i) { + ensureExistingParameterAnnotationsUnpacked(); + if (!hasParameterAnnotations || i > argTypes.length) { + return null; + } + return paramAnnotations[i]; + } + + public String getArgumentName(final int i) { + return argNames[i]; + } + + public String[] getArgumentNames() { + return argNames.clone(); + } + + public Type getArgumentType(final int i) { + return argTypes[i]; + } + + public Type[] getArgumentTypes() { + return argTypes.clone(); + } + + /** + * @return class that contains this method + */ + public String getClassName() { + return className; + } + + /** + * @return all attributes of this method. + */ + public Attribute[] getCodeAttributes() { + return codeAttrsList.toArray(Attribute.EMPTY_ARRAY); + } + + /** + * @return code exceptions for 'Code' attribute + */ + private CodeException[] getCodeExceptions() { + final int size = exceptionList.size(); + final CodeException[] cExc = new CodeException[size]; + Arrays.setAll(cExc, i -> exceptionList.get(i).getCodeException(super.getConstantPool())); + return cExc; + } + + /* + * @return array of declared exception handlers + */ + public CodeExceptionGen[] getExceptionHandlers() { + return exceptionList.toArray(CodeExceptionGen.EMPTY_ARRAY); + } + + /* + * @return array of thrown exceptions + */ + public String[] getExceptions() { + return throwsList.toArray(ArrayUtils.EMPTY_STRING_ARRAY); + } + + /** + * @return 'Exceptions' attribute of all the exceptions thrown by this method. + */ + private ExceptionTable getExceptionTable(final ConstantPoolGen cp) { + final int size = throwsList.size(); + final int[] ex = new int[size]; + Arrays.setAll(ex, i -> cp.addClass(throwsList.get(i))); + return new ExceptionTable(cp.addUtf8("Exceptions"), 2 + 2 * size, ex, cp.getConstantPool()); + } + + public InstructionList getInstructionList() { + return il; + } + + /* + * @return array of line numbers + */ + public LineNumberGen[] getLineNumbers() { + return lineNumberList.toArray(LineNumberGen.EMPTY_ARRAY); + } + + /** + * @return 'LineNumberTable' attribute of all the local variables of this method. + */ + public LineNumberTable getLineNumberTable(final ConstantPoolGen cp) { + final int size = lineNumberList.size(); + final LineNumber[] ln = new LineNumber[size]; + Arrays.setAll(ln, i -> lineNumberList.get(i).getLineNumber()); + return new LineNumberTable(cp.addUtf8("LineNumberTable"), 2 + ln.length * 4, ln, cp.getConstantPool()); + } + + /* + * If the range of the variable has not been set yet, it will be set to be valid from the start to the end of the + * instruction list. + * + * @return array of declared local variables sorted by index + */ + public LocalVariableGen[] getLocalVariables() { + final int size = variableList.size(); + final LocalVariableGen[] lg = new LocalVariableGen[size]; + variableList.toArray(lg); + for (int i = 0; i < size; i++) { + if (lg[i].getStart() == null && il != null) { + lg[i].setStart(il.getStart()); + } + if (lg[i].getEnd() == null && il != null) { + lg[i].setEnd(il.getEnd()); + } + } + if (size > 1) { + Arrays.sort(lg, Comparator.comparingInt(LocalVariableGen::getIndex)); + } + return lg; + } + + /** + * @return 'LocalVariableTable' attribute of all the local variables of this method. + */ + public LocalVariableTable getLocalVariableTable(final ConstantPoolGen cp) { + final LocalVariableGen[] lg = getLocalVariables(); + final int size = lg.length; + final LocalVariable[] lv = new LocalVariable[size]; + Arrays.setAll(lv, i -> lg[i].getLocalVariable(cp)); + return new LocalVariableTable(cp.addUtf8("LocalVariableTable"), 2 + lv.length * 10, lv, cp.getConstantPool()); + } + + /** + * @return 'LocalVariableTypeTable' attribute of this method. + */ + public LocalVariableTypeTable getLocalVariableTypeTable() { + return localVariableTypeTable; + } + + public int getMaxLocals() { + return maxLocals; + } + + public int getMaxStack() { + return maxStack; + } + + /** + * Get method object. Never forget to call setMaxStack() or setMaxStack(max), respectively, before calling this method + * (the same applies for max locals). + * + * @return method object + */ + public JavaMethod getMethod() { + final String signature = getSignature(); + final ConstantPoolGen cp = super.getConstantPool(); + final int nameIndex = cp.addUtf8(super.getName()); + final int signatureIndex = cp.addUtf8(signature); + /* + * Also updates positions of instructions, i.e., their indices + */ + final byte[] byteCode = il != null ? il.getByteCode() : null; + LineNumberTable lnt = null; + LocalVariableTable lvt = null; + /* + * Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.) + */ + if (!variableList.isEmpty() && !stripAttributes) { + updateLocalVariableTable(getLocalVariableTable(cp)); + addCodeAttribute(lvt = getLocalVariableTable(cp)); + } + if (localVariableTypeTable != null) { + // LocalVariable length in LocalVariableTypeTable is not updated automatically. It's a difference with + // LocalVariableTable. + if (lvt != null) { + adjustLocalVariableTypeTable(lvt); + } + addCodeAttribute(localVariableTypeTable); + } + if (!lineNumberList.isEmpty() && !stripAttributes) { + addCodeAttribute(lnt = getLineNumberTable(cp)); + } + final Attribute[] codeAttrs = getCodeAttributes(); + /* + * Each attribute causes 6 additional header bytes + */ + int attrsLen = 0; + for (final Attribute codeAttr : codeAttrs) { + attrsLen += codeAttr.getLength() + 6; + } + final CodeException[] cExc = getCodeExceptions(); + final int excLen = cExc.length * 8; // Every entry takes 8 bytes + Code code = null; + if (byteCode != null && !isAbstract() && !isNative()) { + // Remove any stale code attribute + final Attribute[] attributes = getAttributes(); + for (final Attribute a : attributes) { + if (a instanceof Code) { + removeAttribute(a); + } + } + code = new Code(cp.addUtf8("Code"), 8 + byteCode.length + // prologue byte code + 2 + excLen + // exceptions + 2 + attrsLen, // attributes + maxStack, maxLocals, byteCode, cExc, codeAttrs, cp.getConstantPool()); + addAttribute(code); + } + final Attribute[] annotations = addRuntimeAnnotationsAsAttribute(cp); + final Attribute[] parameterAnnotations = addRuntimeParameterAnnotationsAsAttribute(cp); + ExceptionTable et = null; + if (!throwsList.isEmpty()) { + addAttribute(et = getExceptionTable(cp)); + // Add 'Exceptions' if there are "throws" clauses + } + final JavaMethod m = new JavaMethod(super.getAccessFlags(), nameIndex, signatureIndex, getAttributes(), cp.getConstantPool()); + // Undo effects of adding attributes + if (lvt != null) { + removeCodeAttribute(lvt); + } + if (localVariableTypeTable != null) { + removeCodeAttribute(localVariableTypeTable); + } + if (lnt != null) { + removeCodeAttribute(lnt); + } + if (code != null) { + removeAttribute(code); + } + if (et != null) { + removeAttribute(et); + } + removeRuntimeAttributes(annotations); + removeRuntimeAttributes(parameterAnnotations); + return m; + } + + public Type getReturnType() { + return getType(); + } + + @Override + public String getSignature() { + return Type.getMethodSignature(super.getType(), argTypes); + } + + /** + * Return value as defined by given BCELComparator strategy. By default return the hashcode of the method's name XOR + * signature. + * + * @see Object#hashCode() + */ + @Override + public int hashCode() { + return bcelComparator.hashCode(this); + } + + private List makeMutableVersion(final AnnotationEntry[] mutableArray) { + final List result = new ArrayList<>(); + for (final AnnotationEntry element : mutableArray) { + result.add(new AnnotationEntryGen(element, getConstantPool(), false)); + } + return result; + } + + /** + * Remove a code attribute. + */ + public void removeCodeAttribute(final Attribute a) { + codeAttrsList.remove(a); + } + + /** + * Remove all code attributes. + */ + public void removeCodeAttributes() { + localVariableTypeTable = null; + codeAttrsList.clear(); + } + + /** + * Remove an exception. + */ + public void removeException(final String c) { + throwsList.remove(c); + } + + /** + * Remove an exception handler. + */ + public void removeExceptionHandler(final CodeExceptionGen c) { + exceptionList.remove(c); + } + + /** + * Remove all line numbers. + */ + public void removeExceptionHandlers() { + exceptionList.clear(); + } + + /** + * Remove all exceptions. + */ + public void removeExceptions() { + throwsList.clear(); + } + + /** + * Remove a line number. + */ + public void removeLineNumber(final LineNumberGen l) { + lineNumberList.remove(l); + } + + /** + * Remove all line numbers. + */ + public void removeLineNumbers() { + lineNumberList.clear(); + } + + /** + * Remove a local variable, its slot will not be reused, if you do not use addLocalVariable with an explicit index + * argument. + */ + public void removeLocalVariable(final LocalVariableGen l) { + l.dispose(); + variableList.remove(l); + } + + /** + * Remove all local variables. + */ + public void removeLocalVariables() { + variableList.forEach(LocalVariableGen::dispose); + variableList.clear(); + } + + /** + * Remove the LocalVariableTypeTable + */ + public void removeLocalVariableTypeTable() { + localVariableTypeTable = null; + } + + /** + * Remove all NOPs from the instruction list (if possible) and update every object referring to them, i.e., branch + * instructions, local variables and exception handlers. + */ + public void removeNOPs() { + if (il != null) { + InstructionHandle next; + /* + * Check branch instructions. + */ + for (InstructionHandle ih = il.getStart(); ih != null; ih = next) { + next = ih.getNext(); + if (next != null && ih.getInstruction() instanceof NOP) { + try { + il.delete(ih); + } catch (final TargetLostException e) { + for (final InstructionHandle target : e.getTargets()) { + for (final InstructionTargeter targeter : target.getTargeters()) { + targeter.updateTarget(target, next); + } + } + } + } + } + } + } + + /** + * Remove observer for this object. + */ + public void removeObserver(final MethodObserver o) { + if (observers != null) { + observers.remove(o); + } + } + + /** + * Would prefer to make this private, but need a way to test if client is using BCEL version 6.5.0 or later that + * contains fix for BCEL-329. + * + * @since 6.5.0 + */ + public void removeRuntimeAttributes(final Attribute[] attrs) { + for (final Attribute attr : attrs) { + removeAttribute(attr); + } + } + + public void setArgumentName(final int i, final String name) { + argNames[i] = name; + } + + public void setArgumentNames(final String[] argNames) { + this.argNames = argNames; + } + + public void setArgumentType(final int i, final Type type) { + argTypes[i] = type; + } + + public void setArgumentTypes(final Type[] argTypes) { + this.argTypes = argTypes; + } + + public void setClassName(final String className) { // TODO could be package-protected? + this.className = className; + } + + public void setInstructionList(final InstructionList il) { // TODO could be package-protected? + this.il = il; + } + + /** + * Compute maximum number of local variables. + */ + public void setMaxLocals() { // TODO could be package-protected? (some tests would need repackaging) + if (il != null) { + int max = isStatic() ? 0 : 1; + if (argTypes != null) { + for (final Type argType : argTypes) { + max += argType.getSize(); + } + } + for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) { + final Instruction ins = ih.getInstruction(); + if (ins instanceof LocalVariableInstruction || ins instanceof RET || ins instanceof IINC) { + final int index = ((IndexedInstruction) ins).getIndex() + ((TypedInstruction) ins).getType(super.getConstantPool()).getSize(); + if (index > max) { + max = index; + } + } + } + maxLocals = max; + } else { + maxLocals = 0; + } + } + + /** + * Set maximum number of local variables. + */ + public void setMaxLocals(final int m) { + maxLocals = m; + } + + /** + * Computes max. stack size by performing control flow analysis. + */ + public void setMaxStack() { // TODO could be package-protected? (some tests would need repackaging) + if (il != null) { + maxStack = getMaxStack(super.getConstantPool(), il, getExceptionHandlers()); + } else { + maxStack = 0; + } + } + + /** + * Set maximum stack size for this method. + */ + public void setMaxStack(final int m) { // TODO could be package-protected? + maxStack = m; + } + + public void setReturnType(final Type returnType) { + setType(returnType); + } + + /** + * Do not/Do produce attributes code attributesLineNumberTable and LocalVariableTable, like javac -O + */ + public void stripAttributes(final boolean flag) { + stripAttributes = flag; + } + + /** + * Return string representation close to declaration format, 'public static void main(String[]) throws IOException', + * e.g. + * + * @return String representation of the method. + */ + @Override + public final String toString() { + final String access = Utility.accessToString(super.getAccessFlags()); + String signature = Type.getMethodSignature(super.getType(), argTypes); + signature = Utility.methodSignatureToString(signature, super.getName(), access, true, getLocalVariableTable(super.getConstantPool())); + final StringBuilder buf = new StringBuilder(signature); + for (final Attribute a : getAttributes()) { + if (!(a instanceof Code || a instanceof ExceptionTable)) { + buf.append(" [").append(a).append("]"); + } + } + + if (!throwsList.isEmpty()) { + for (final String throwsDescriptor : throwsList) { + buf.append("\n\t\tthrows ").append(throwsDescriptor); + } + } + return buf.toString(); + } + + /** + * Call notify() method on all observers. This method is not called automatically whenever the state has changed, but + * has to be called by the user after they have finished editing the object. + */ + public void update() { + if (observers != null) { + for (final MethodObserver observer : observers) { + observer.notify(this); + } + } + } + + private void updateLocalVariableTable(final LocalVariableTable a) { + removeLocalVariables(); + for (final LocalVariable l : a.getLocalVariableTable()) { + InstructionHandle start = il.findHandle(l.getStartPC()); + final InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength()); + // Repair malformed handles + if (null == start) { + start = il.getStart(); + } + // end == null => live to end of method + // Since we are recreating the LocalVaraible, we must + // propagate the orig_index to new copy. + addLocalVariable(l.getName(), Type.getType(l.getSignature()), l.getIndex(), start, end, l.getOrigIndex()); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/MethodObserver.java b/src/main/java/haidnor/jvm/bcel/generic/MethodObserver.java new file mode 100644 index 0000000..0b510cc --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/MethodObserver.java @@ -0,0 +1,26 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Implement this interface if you're interested in changes to a MethodGen object and register yourself with + * addObserver(). + */ +public interface MethodObserver { + + void notify(MethodGen method); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/NEW.java b/src/main/java/haidnor/jvm/bcel/generic/NEW.java new file mode 100644 index 0000000..9c7d0e8 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/NEW.java @@ -0,0 +1,68 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * NEW - Create new object + * + *
+ * Stack: ... -> ..., objectref
+ * 
+ */ +public class NEW extends CPInstruction implements LoadClass, AllocationInstruction, ExceptionThrower, StackProducer { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + NEW() { + } + + public NEW(final int index) { + super(Const.NEW, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitLoadClass(this); + v.visitAllocationInstruction(this); + v.visitExceptionThrower(this); + v.visitStackProducer(this); + v.visitTypedInstruction(this); + v.visitCPInstruction(this); + v.visitNEW(this); + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_CLASS_AND_INTERFACE_RESOLUTION, ExceptionConst.ILLEGAL_ACCESS_ERROR, + ExceptionConst.INSTANTIATION_ERROR); + } + + @Override + public ObjectType getLoadClassType(final ConstantPoolGen cpg) { + return (ObjectType) getType(cpg); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/NEWARRAY.java b/src/main/java/haidnor/jvm/bcel/generic/NEWARRAY.java new file mode 100644 index 0000000..4e8514a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/NEWARRAY.java @@ -0,0 +1,114 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * NEWARRAY - Create new array of basic type (int, short, ...) + * + *
+ * Stack: ..., count -> ..., arrayref
+ * 
+ * + * type must be one of T_INT, T_SHORT, ... + */ +public class NEWARRAY extends Instruction implements AllocationInstruction, ExceptionThrower, StackProducer { + + private byte type; + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + NEWARRAY() { + } + + public NEWARRAY(final BasicType type) { + this(type.getType()); + } + + public NEWARRAY(final byte type) { + super(Const.NEWARRAY, (short) 2); + this.type = type; + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitAllocationInstruction(this); + v.visitExceptionThrower(this); + v.visitStackProducer(this); + v.visitNEWARRAY(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(super.getOpcode()); + out.writeByte(type); + } + + @Override + public Class[] getExceptions() { + return new Class[] {ExceptionConst.NEGATIVE_ARRAY_SIZE_EXCEPTION}; + } + + /** + * @return type of constructed array + */ + public final Type getType() { + return new ArrayType(BasicType.getType(type), 1); + } + + /** + * @return numeric code for basic element type + */ + public final byte getTypecode() { + return type; + } + + /** + * Read needed data (e.g. index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + type = bytes.readByte(); + super.setLength(2); + } + + /** + * @return mnemonic for instruction + */ + @Override + public String toString(final boolean verbose) { + return super.toString(verbose) + " " + Const.getTypeName(type); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/NOP.java b/src/main/java/haidnor/jvm/bcel/generic/NOP.java new file mode 100644 index 0000000..6998e37 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/NOP.java @@ -0,0 +1,40 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * NOP - Do nothing + */ +public class NOP extends Instruction { + + public NOP() { + super(Const.NOP, (short) 1); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitNOP(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/NameSignatureInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/NameSignatureInstruction.java new file mode 100644 index 0000000..7ca9c0a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/NameSignatureInstruction.java @@ -0,0 +1,63 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.ConstantCP; +import haidnor.jvm.bcel.classfile.ConstantNameAndType; +import haidnor.jvm.bcel.classfile.ConstantPool; +import haidnor.jvm.bcel.classfile.ConstantUtf8; + +/** + * Super class for FieldOrMethod and INVOKEDYNAMIC, since they both have names and signatures + * + * @since 6.0 + */ +public abstract class NameSignatureInstruction extends CPInstruction { + + public NameSignatureInstruction() { + } + + public NameSignatureInstruction(final short opcode, final int index) { + super(opcode, index); + } + + /** + * @return name of referenced method/field. + */ + public String getName(final ConstantPoolGen cpg) { + final ConstantPool cp = cpg.getConstantPool(); + final ConstantNameAndType cnat = getNameAndType(cpg); + return ((ConstantUtf8) cp.getConstant(cnat.getNameIndex())).getBytes(); + } + + public ConstantNameAndType getNameAndType(final ConstantPoolGen cpg) { + final ConstantPool cp = cpg.getConstantPool(); + final ConstantCP cmr = (ConstantCP) cp.getConstant(super.getIndex()); + return (ConstantNameAndType) cp.getConstant(cmr.getNameAndTypeIndex()); + } + + /** + * @return signature of referenced method/field. + */ + public String getSignature(final ConstantPoolGen cpg) { + final ConstantPool cp = cpg.getConstantPool(); + final ConstantNameAndType cnat = getNameAndType(cpg); + return ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes(); + } + +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/NamedAndTyped.java b/src/main/java/haidnor/jvm/bcel/generic/NamedAndTyped.java new file mode 100644 index 0000000..dd76de8 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/NamedAndTyped.java @@ -0,0 +1,31 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denote entity that has both name and type. This is true for local variables, methods and fields. + */ +public interface NamedAndTyped { + + String getName(); + + Type getType(); + + void setName(String name); + + void setType(Type type); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ObjectType.java b/src/main/java/haidnor/jvm/bcel/generic/ObjectType.java new file mode 100644 index 0000000..0ccb1d8 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ObjectType.java @@ -0,0 +1,154 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.Repository; +import haidnor.jvm.bcel.classfile.JavaClass; +import haidnor.jvm.bcel.classfile.Utility; + +/** + * Denotes reference such as java.lang.String. + */ +public class ObjectType extends ReferenceType { + + /** + * Constructs a new instance. + * + * @param className fully qualified class name, e.g. java.lang.String + * @return a new instance. + * @since 6.0 + */ + public static ObjectType getInstance(final String className) { + return new ObjectType(className); + } + + private final String className; // Class name of type + + /** + * Constructs a new instance. + * + * @param className fully qualified class name, e.g. java.lang.String + */ + public ObjectType(final String className) { + super(Const.T_REFERENCE, "L" + Utility.packageToPath(className) + ";"); + this.className = Utility.pathToPackage(className); + } + + /** + * Java Virtual Machine Specification edition 2, � 5.4.4 Access Control + * + * @throws ClassNotFoundException if the class referenced by this type can't be found + */ + public boolean accessibleTo(final ObjectType accessor) throws ClassNotFoundException { + final JavaClass jc = Repository.lookupClass(className); + if (jc.isPublic()) { + return true; + } + final JavaClass acc = Repository.lookupClass(accessor.className); + return acc.getPackageName().equals(jc.getPackageName()); + } + + /** + * @return true if both type objects refer to the same class. + */ + @Override + public boolean equals(final Object type) { + return type instanceof ObjectType && ((ObjectType) type).className.equals(className); + } + + /** + * @return name of referenced class + */ + @Override + public String getClassName() { + return className; + } + + /** + * @return a hash code value for the object. + */ + @Override + public int hashCode() { + return className.hashCode(); + } + + /** + * If "this" doesn't reference a class, it references an interface or a non-existant entity. + * @deprecated (since 6.0) this method returns an inaccurate result if the class or interface referenced cannot be + * found: use referencesClassExact() instead + */ + @Deprecated + public boolean referencesClass() { + try { + final JavaClass jc = Repository.lookupClass(className); + return jc.isClass(); + } catch (final ClassNotFoundException e) { + return false; + } + } + + /** + * Return true if this type references a class, false if it references an interface. + * + * @return true if the type references a class, false if it references an interface + * @throws ClassNotFoundException if the class or interface referenced by this type can't be found + */ + public boolean referencesClassExact() throws ClassNotFoundException { + final JavaClass jc = Repository.lookupClass(className); + return jc.isClass(); + } + + /** + * If "this" doesn't reference an interface, it references a class or a non-existant entity. + * + * @deprecated (since 6.0) this method returns an inaccurate result if the class or interface referenced cannot be + * found: use referencesInterfaceExact() instead + */ + @Deprecated + public boolean referencesInterface() { + try { + final JavaClass jc = Repository.lookupClass(className); + return !jc.isClass(); + } catch (final ClassNotFoundException e) { + return false; + } + } + + /** + * Return true if this type references an interface, false if it references a class. + * + * @return true if the type references an interface, false if it references a class + * @throws ClassNotFoundException if the class or interface referenced by this type can't be found + */ + public boolean referencesInterfaceExact() throws ClassNotFoundException { + final JavaClass jc = Repository.lookupClass(className); + return !jc.isClass(); + } + + /** + * Return true if this type is a subclass of given ObjectType. + * + * @throws ClassNotFoundException if any of this class's superclasses can't be found + */ + public boolean subclassOf(final ObjectType superclass) throws ClassNotFoundException { + if (this.referencesInterfaceExact() || superclass.referencesInterfaceExact()) { + return false; + } + return Repository.instanceOf(this.className, superclass.className); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/POP.java b/src/main/java/haidnor/jvm/bcel/generic/POP.java new file mode 100644 index 0000000..c1313c1 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/POP.java @@ -0,0 +1,47 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * POP - Pop top operand stack word + * + *
+ * Stack: ..., word -> ...
+ * 
+ */ +public class POP extends StackInstruction implements PopInstruction { + + public POP() { + super(Const.POP); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitPopInstruction(this); + v.visitStackInstruction(this); + v.visitPOP(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/POP2.java b/src/main/java/haidnor/jvm/bcel/generic/POP2.java new file mode 100644 index 0000000..cd79683 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/POP2.java @@ -0,0 +1,47 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * POP2 - Pop two top operand stack words + * + *
+ * Stack: ..., word2, word1 -> ...
+ * 
+ */ +public class POP2 extends StackInstruction implements PopInstruction { + + public POP2() { + super(Const.POP2); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitPopInstruction(this); + v.visitStackInstruction(this); + v.visitPOP2(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/PUSH.java b/src/main/java/haidnor/jvm/bcel/generic/PUSH.java new file mode 100644 index 0000000..86dcd09 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/PUSH.java @@ -0,0 +1,195 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +import java.util.Objects; + +/** + * Wrapper class for push operations, which are implemented either as BIPUSH, LDC or xCONST_n instructions. + */ +public final class PUSH implements CompoundInstruction, VariableLengthInstruction { + + private final Instruction instruction; + + /** + * Pushes an array type constant, for example {@code int[].class}, {@code String[].class}, and so on. + * + * @param cp generated constant pool. + * @param value to be pushed. + * @since 6.7.0 + */ + public PUSH(final ConstantPoolGen cp, final ArrayType value) { + if (value == null) { + instruction = InstructionConst.ACONST_NULL; + } else { + instruction = new LDC(cp.addArrayClass(value)); + } + } + + /** + * @param cp Constant pool + * @param value to be pushed + */ + public PUSH(final ConstantPoolGen cp, final boolean value) { + Objects.requireNonNull(cp, "cp"); + instruction = InstructionConst.getInstruction(Const.ICONST_0 + (value ? 1 : 0)); + } + + /** + * @param cp Constant pool + * @param value to be pushed + */ + public PUSH(final ConstantPoolGen cp, final Boolean value) { + this(cp, value.booleanValue()); + } + + /** + * creates a push object from a Character value. Warning: Make sure not to attempt to allow autoboxing to create this + * value parameter, as an alternative constructor will be called + * + * @param cp Constant pool + * @param value to be pushed + */ + public PUSH(final ConstantPoolGen cp, final Character value) { + this(cp, value.charValue()); + } + + /** + * @param cp Constant pool + * @param value to be pushed + */ + public PUSH(final ConstantPoolGen cp, final double value) { + if (value == 0.0) { + instruction = InstructionConst.DCONST_0; + } else if (value == 1.0) { + instruction = InstructionConst.DCONST_1; + } else { + instruction = new LDC2_W(cp.addDouble(value)); + } + } + + /** + * @param cp Constant pool + * @param value to be pushed + */ + public PUSH(final ConstantPoolGen cp, final float value) { + if (value == 0.0) { + instruction = InstructionConst.FCONST_0; + } else if (value == 1.0) { + instruction = InstructionConst.FCONST_1; + } else if (value == 2.0) { + instruction = InstructionConst.FCONST_2; + } else { + instruction = new LDC(cp.addFloat(value)); + } + } + + /** + * This constructor also applies for values of type short, char, byte + * + * @param cp Constant pool + * @param value to be pushed + */ + public PUSH(final ConstantPoolGen cp, final int value) { + if (value >= -1 && value <= 5) { + instruction = InstructionConst.getInstruction(Const.ICONST_0 + value); + } else if (Instruction.isValidByte(value)) { + instruction = new BIPUSH((byte) value); + } else if (Instruction.isValidShort(value)) { + instruction = new SIPUSH((short) value); + } else { + instruction = new LDC(cp.addInteger(value)); + } + } + + /** + * @param cp Constant pool + * @param value to be pushed + */ + public PUSH(final ConstantPoolGen cp, final long value) { + if (value == 0) { + instruction = InstructionConst.LCONST_0; + } else if (value == 1) { + instruction = InstructionConst.LCONST_1; + } else { + instruction = new LDC2_W(cp.addLong(value)); + } + } + + /** + * @param cp Constant pool + * @param value to be pushed + */ + public PUSH(final ConstantPoolGen cp, final Number value) { + if (value instanceof Integer || value instanceof Short || value instanceof Byte) { + instruction = new PUSH(cp, value.intValue()).instruction; + } else if (value instanceof Double) { + instruction = new PUSH(cp, value.doubleValue()).instruction; + } else if (value instanceof Float) { + instruction = new PUSH(cp, value.floatValue()).instruction; + } else if (value instanceof Long) { + instruction = new PUSH(cp, value.longValue()).instruction; + } else { + throw new ClassGenException("What's this: " + value); + } + } + + /** + * + * @param cp + * @param value + * @since 6.0 + */ + public PUSH(final ConstantPoolGen cp, final ObjectType value) { + if (value == null) { + instruction = InstructionConst.ACONST_NULL; + } else { + instruction = new LDC(cp.addClass(value)); + } + } + + /** + * @param cp Constant pool + * @param value to be pushed + */ + public PUSH(final ConstantPoolGen cp, final String value) { + if (value == null) { + instruction = InstructionConst.ACONST_NULL; + } else { + instruction = new LDC(cp.addString(value)); + } + } + + public Instruction getInstruction() { + return instruction; + } + + @Override + public InstructionList getInstructionList() { + return new InstructionList(instruction); + } + + /** + * @return mnemonic for instruction + */ + @Override + public String toString() { + return instruction + " (PUSH)"; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/PUTFIELD.java b/src/main/java/haidnor/jvm/bcel/generic/PUTFIELD.java new file mode 100644 index 0000000..be1688c --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/PUTFIELD.java @@ -0,0 +1,76 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * PUTFIELD - Put field in object + * + *
+ * Stack: ..., objectref, value -> ...
+ * 
+ * + * OR + * + *
+ * Stack: ..., objectref, value.word1, value.word2 -> ...
+ * 
+ */ +public class PUTFIELD extends FieldInstruction implements PopInstruction, ExceptionThrower { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + PUTFIELD() { + } + + public PUTFIELD(final int index) { + super(Const.PUTFIELD, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitStackConsumer(this); + v.visitPopInstruction(this); + v.visitTypedInstruction(this); + v.visitLoadClass(this); + v.visitCPInstruction(this); + v.visitFieldOrMethod(this); + v.visitFieldInstruction(this); + v.visitPUTFIELD(this); + } + + @Override + public int consumeStack(final ConstantPoolGen cpg) { + return getFieldSize(cpg) + 1; + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_FIELD_AND_METHOD_RESOLUTION, ExceptionConst.NULL_POINTER_EXCEPTION, + ExceptionConst.INCOMPATIBLE_CLASS_CHANGE_ERROR); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/PUTSTATIC.java b/src/main/java/haidnor/jvm/bcel/generic/PUTSTATIC.java new file mode 100644 index 0000000..800c170 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/PUTSTATIC.java @@ -0,0 +1,75 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * PUTSTATIC - Put static field in class + * + *
+ * Stack: ..., value -> ...
+ * 
+ * + * OR + * + *
+ * Stack: ..., value.word1, value.word2 -> ...
+ * 
+ */ +public class PUTSTATIC extends FieldInstruction implements ExceptionThrower, PopInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + PUTSTATIC() { + } + + public PUTSTATIC(final int index) { + super(Const.PUTSTATIC, index); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitStackConsumer(this); + v.visitPopInstruction(this); + v.visitTypedInstruction(this); + v.visitLoadClass(this); + v.visitCPInstruction(this); + v.visitFieldOrMethod(this); + v.visitFieldInstruction(this); + v.visitPUTSTATIC(this); + } + + @Override + public int consumeStack(final ConstantPoolGen cpg) { + return getFieldSize(cpg); + } + + @Override + public Class[] getExceptions() { + return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_FIELD_AND_METHOD_RESOLUTION, ExceptionConst.INCOMPATIBLE_CLASS_CHANGE_ERROR); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/PopInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/PopInstruction.java new file mode 100644 index 0000000..e3380b8 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/PopInstruction.java @@ -0,0 +1,26 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denotes an unparameterized instruction to pop a value on top from the stack, such as ISTORE, POP, PUTSTATIC. + * + * @see ISTORE + * @see POP + */ +public interface PopInstruction extends StackConsumer { +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/PushInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/PushInstruction.java new file mode 100644 index 0000000..b3cdb52 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/PushInstruction.java @@ -0,0 +1,32 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denotes an unparameterized instruction to produce a value on top of the stack, such as ILOAD, LDC, SIPUSH, DUP, + * ICONST, etc. + * + * + * @see ILOAD + * @see ICONST + * @see LDC + * @see DUP + * @see SIPUSH + * @see GETSTATIC + */ +public interface PushInstruction extends StackProducer { +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/RET.java b/src/main/java/haidnor/jvm/bcel/generic/RET.java new file mode 100644 index 0000000..665de71 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/RET.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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * RET - Return from subroutine + * + *
+ * Stack: ... -> ...
+ * 
+ */ +public class RET extends Instruction implements IndexedInstruction, TypedInstruction { + + private boolean wide; + private int index; // index to local variable containg the return address + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + RET() { + } + + public RET(final int index) { + super(Const.RET, (short) 2); + setIndex(index); // May set wide as side effect + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitRET(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + if (wide) { + out.writeByte(Const.WIDE); + } + out.writeByte(super.getOpcode()); + if (wide) { + out.writeShort(index); + } else { + out.writeByte(index); + } + } + + /** + * @return index of local variable containg the return address + */ + @Override + public final int getIndex() { + return index; + } + + /** + * @return return address type + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return ReturnaddressType.NO_TARGET; + } + + /** + * Read needed data (e.g. index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + this.wide = wide; + if (wide) { + index = bytes.readUnsignedShort(); + super.setLength(4); + } else { + index = bytes.readUnsignedByte(); + super.setLength(2); + } + } + + /** + * Set index of local variable containg the return address + */ + @Override + public final void setIndex(final int n) { + if (n < 0) { + throw new ClassGenException("Negative index value: " + n); + } + index = n; + setWide(); + } + + private void setWide() { + wide = index > Const.MAX_BYTE; + if (wide) { + super.setLength(4); // Including the wide byte + } else { + super.setLength(2); + } + } + + /** + * @return mnemonic for instruction + */ + @Override + public String toString(final boolean verbose) { + return super.toString(verbose) + " " + index; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/RETURN.java b/src/main/java/haidnor/jvm/bcel/generic/RETURN.java new file mode 100644 index 0000000..7b644de --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/RETURN.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * RETURN - Return from void method + * + *
+ * Stack: ... -> <empty>
+ * 
+ */ +public class RETURN extends ReturnInstruction { + + public RETURN() { + super(Const.RETURN); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitStackConsumer(this); + v.visitReturnInstruction(this); + v.visitRETURN(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ReferenceType.java b/src/main/java/haidnor/jvm/bcel/generic/ReferenceType.java new file mode 100644 index 0000000..a681c7c --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ReferenceType.java @@ -0,0 +1,260 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.Repository; +import haidnor.jvm.bcel.classfile.JavaClass; + +/** + * Super class for object and array types. + */ +public abstract class ReferenceType extends Type { + + /** + * Class is non-abstract but not instantiable from the outside + */ + ReferenceType() { + super(Const.T_OBJECT, ""); + } + + protected ReferenceType(final byte t, final String s) { + super(t, s); + } + + /** + * This commutative operation returns the first common superclass (narrowest ReferenceType referencing a class, not an + * interface). If one of the types is a superclass of the other, the former is returned. If "this" is Type.NULL, then t + * is returned. If t is Type.NULL, then "this" is returned. If "this" equals t ['this.equals(t)'] "this" is returned. If + * "this" or t is an ArrayType, then Type.OBJECT is returned. If "this" or t is a ReferenceType referencing an + * interface, then Type.OBJECT is returned. If not all of the two classes' superclasses cannot be found, "null" is + * returned. See the JVM specification edition 2, "�4.9.2 The Bytecode Verifier". + * + * @deprecated use getFirstCommonSuperclass(ReferenceType t) which has slightly changed semantics. + * @throws ClassNotFoundException on failure to find superclasses of this type, or the type passed as a parameter + */ + @Deprecated + public ReferenceType firstCommonSuperclass(final ReferenceType t) throws ClassNotFoundException { + if (this.equals(Type.NULL)) { + return t; + } + if (t.equals(Type.NULL) || this.equals(t)) { + return this; + /* + * TODO: Above sounds a little arbitrary. On the other hand, there is no object referenced by Type.NULL so we can also + * say all the objects referenced by Type.NULL were derived from java.lang.Object. However, the Java Language's + * "instanceof" operator proves us wrong: "null" is not referring to an instance of java.lang.Object :) + */ + } + if (this instanceof ArrayType || t instanceof ArrayType) { + return Type.OBJECT; + // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType? + } + return getFirstCommonSuperclassInternal(t); + } + + /** + * This commutative operation returns the first common superclass (narrowest ReferenceType referencing a class, not an + * interface). If one of the types is a superclass of the other, the former is returned. If "this" is Type.NULL, then t + * is returned. If t is Type.NULL, then "this" is returned. If "this" equals t ['this.equals(t)'] "this" is returned. If + * "this" or t is an ArrayType, then Type.OBJECT is returned; unless their dimensions match. Then an ArrayType of the + * same number of dimensions is returned, with its basic type being the first common super class of the basic types of + * "this" and t. If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned. If not all of + * the two classes' superclasses cannot be found, "null" is returned. See the JVM specification edition 2, "�4.9.2 The + * Bytecode Verifier". + * + * @throws ClassNotFoundException on failure to find superclasses of this type, or the type passed as a parameter + */ + public ReferenceType getFirstCommonSuperclass(final ReferenceType t) throws ClassNotFoundException { + if (this.equals(Type.NULL)) { + return t; + } + if (t.equals(Type.NULL) || this.equals(t)) { + return this; + /* + * TODO: Above sounds a little arbitrary. On the other hand, there is no object referenced by Type.NULL so we can also + * say all the objects referenced by Type.NULL were derived from java.lang.Object. However, the Java Language's + * "instanceof" operator proves us wrong: "null" is not referring to an instance of java.lang.Object :) + */ + } + /* This code is from a bug report by Konstantin Shagin */ + if (this instanceof ArrayType && t instanceof ArrayType) { + final ArrayType arrType1 = (ArrayType) this; + final ArrayType arrType2 = (ArrayType) t; + if (arrType1.getDimensions() == arrType2.getDimensions() && arrType1.getBasicType() instanceof ObjectType + && arrType2.getBasicType() instanceof ObjectType) { + return new ArrayType(((ObjectType) arrType1.getBasicType()).getFirstCommonSuperclass((ObjectType) arrType2.getBasicType()), + arrType1.getDimensions()); + } + } + if (this instanceof ArrayType || t instanceof ArrayType) { + return Type.OBJECT; + // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType? + } + return getFirstCommonSuperclassInternal(t); + } + + private ReferenceType getFirstCommonSuperclassInternal(final ReferenceType t) throws ClassNotFoundException { + if (this instanceof ObjectType && ((ObjectType) this).referencesInterfaceExact() + || t instanceof ObjectType && ((ObjectType) t).referencesInterfaceExact()) { + return Type.OBJECT; + // TODO: The above line is correct comparing to the vmspec2. But one could + // make class file verification a bit stronger here by using the notion of + // superinterfaces or even castability or assignment compatibility. + } + // this and t are ObjectTypes, see above. + final ObjectType thiz = (ObjectType) this; + final ObjectType other = (ObjectType) t; + final JavaClass[] thizSups = Repository.getSuperClasses(thiz.getClassName()); + final JavaClass[] otherSups = Repository.getSuperClasses(other.getClassName()); + if (thizSups == null || otherSups == null) { + return null; + } + // Waaahh... + final JavaClass[] thisSups = new JavaClass[thizSups.length + 1]; + final JavaClass[] tSups = new JavaClass[otherSups.length + 1]; + System.arraycopy(thizSups, 0, thisSups, 1, thizSups.length); + System.arraycopy(otherSups, 0, tSups, 1, otherSups.length); + thisSups[0] = Repository.lookupClass(thiz.getClassName()); + tSups[0] = Repository.lookupClass(other.getClassName()); + for (final JavaClass tSup : tSups) { + for (final JavaClass thisSup : thisSups) { + if (thisSup.equals(tSup)) { + return ObjectType.getInstance(thisSup.getClassName()); + } + } + } + // Huh? Did you ask for Type.OBJECT's superclass?? + return null; + } + + /** + * Return true iff this is assignment compatible with another type t as defined in the JVM specification; see the + * AASTORE definition there. + * + * @throws ClassNotFoundException if any classes or interfaces required to determine assignment compatibility can't be + * found + */ + public boolean isAssignmentCompatibleWith(final Type t) throws ClassNotFoundException { + if (!(t instanceof ReferenceType)) { + return false; + } + final ReferenceType T = (ReferenceType) t; + if (this.equals(Type.NULL)) { + return true; // This is not explicitly stated, but clear. Isn't it? + } + /* + * If this is a class type then + */ + if (this instanceof ObjectType && ((ObjectType) this).referencesClassExact()) { + /* + * If T is a class type, then this must be the same class as T, or this must be a subclass of T; + */ + if (T instanceof ObjectType && ((ObjectType) T).referencesClassExact() + && (this.equals(T) || Repository.instanceOf(((ObjectType) this).getClassName(), ((ObjectType) T).getClassName()))) { + return true; + } + /* + * If T is an interface type, this must implement interface T. + */ + if (T instanceof ObjectType && ((ObjectType) T).referencesInterfaceExact() + && Repository.implementationOf(((ObjectType) this).getClassName(), ((ObjectType) T).getClassName())) { + return true; + } + } + /* + * If this is an interface type, then: + */ + if (this instanceof ObjectType && ((ObjectType) this).referencesInterfaceExact()) { + /* + * If T is a class type, then T must be Object (�2.4.7). + */ + if (T instanceof ObjectType && ((ObjectType) T).referencesClassExact() && T.equals(Type.OBJECT)) { + return true; + } + /* + * If T is an interface type, then T must be the same interface as this or a superinterface of this (�2.13.2). + */ + if (T instanceof ObjectType && ((ObjectType) T).referencesInterfaceExact() + && (this.equals(T) || Repository.implementationOf(((ObjectType) this).getClassName(), ((ObjectType) T).getClassName()))) { + return true; + } + } + /* + * If this is an array type, namely, the type SC[], that is, an array of components of type SC, then: + */ + if (this instanceof ArrayType) { + /* + * If T is a class type, then T must be Object (�2.4.7). + */ + if (T instanceof ObjectType && ((ObjectType) T).referencesClassExact() && T.equals(Type.OBJECT)) { + return true; + } + /* + * If T is an array type TC[], that is, an array of components of type TC, then one of the following must be true: + */ + if (T instanceof ArrayType) { + /* + * TC and SC are the same primitive type (�2.4.1). + */ + final Type sc = ((ArrayType) this).getElementType(); + final Type tc = ((ArrayType) T).getElementType(); + if (sc instanceof BasicType && tc instanceof BasicType && sc.equals(tc)) { + return true; + } + /* + * TC and SC are reference types (�2.4.6), and type SC is assignable to TC by these runtime rules. + */ + if (tc instanceof ReferenceType && sc instanceof ReferenceType && ((ReferenceType) sc).isAssignmentCompatibleWith(tc)) { + return true; + } + } + /* If T is an interface type, T must be one of the interfaces implemented by arrays (�2.15). */ + // TODO: Check if this is still valid or find a way to dynamically find out which + // interfaces arrays implement. However, as of the JVM specification edition 2, there + // are at least two different pages where assignment compatibility is defined and + // on one of them "interfaces implemented by arrays" is exchanged with "'Cloneable' or + // 'java.io.Serializable'" + if (T instanceof ObjectType && ((ObjectType) T).referencesInterfaceExact()) { + for (final String element : Const.getInterfacesImplementedByArrays()) { + if (T.equals(ObjectType.getInstance(element))) { + return true; + } + } + } + } + return false; // default. + } + + /** + * Return true iff this type is castable to another type t as defined in the JVM specification. The case where this is + * Type.NULL is not defined (see the CHECKCAST definition in the JVM specification). However, because e.g. CHECKCAST + * doesn't throw a ClassCastException when casting a null reference to any Object, true is returned in this case. + * + * @throws ClassNotFoundException if any classes or interfaces required to determine assignment compatibility can't be + * found + */ + public boolean isCastableTo(final Type t) throws ClassNotFoundException { + if (this.equals(Type.NULL)) { + return t instanceof ReferenceType; // If this is ever changed in isAssignmentCompatible() + } + return isAssignmentCompatibleWith(t); + /* + * Yes, it's true: It's the same definition. See vmspec2 AASTORE / CHECKCAST definitions. + */ + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ReturnInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/ReturnInstruction.java new file mode 100644 index 0000000..c979b61 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ReturnInstruction.java @@ -0,0 +1,72 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.ExceptionConst; + +/** + * Super class for the xRETURN family of instructions. + */ +public abstract class ReturnInstruction extends Instruction implements ExceptionThrower, TypedInstruction, StackConsumer { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + ReturnInstruction() { + } + + /** + * @param opcode of instruction + */ + protected ReturnInstruction(final short opcode) { + super(opcode, (short) 1); + } + + @Override + public Class[] getExceptions() { + return new Class[] {ExceptionConst.ILLEGAL_MONITOR_STATE}; + } + + public Type getType() { + final short opcode = super.getOpcode(); + switch (opcode) { + case Const.IRETURN: + return Type.INT; + case Const.LRETURN: + return Type.LONG; + case Const.FRETURN: + return Type.FLOAT; + case Const.DRETURN: + return Type.DOUBLE; + case Const.ARETURN: + return Type.OBJECT; + case Const.RETURN: + return Type.VOID; + default: // Never reached + throw new ClassGenException("Unknown type " + opcode); + } + } + + /** + * @return type associated with the instruction + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return getType(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/ReturnaddressType.java b/src/main/java/haidnor/jvm/bcel/generic/ReturnaddressType.java new file mode 100644 index 0000000..8191907 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/ReturnaddressType.java @@ -0,0 +1,78 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * Returnaddress, the type JSR or JSR_W instructions push upon the stack. + * + * see vmspec2 �3.3.3 + */ +public class ReturnaddressType extends Type { + + public static final ReturnaddressType NO_TARGET = new ReturnaddressType(); + private InstructionHandle returnTarget; + + /** + * A Returnaddress [that doesn't know where to return to]. + */ + private ReturnaddressType() { + super(Const.T_ADDRESS, ""); + } + + /** + * Creates a ReturnaddressType object with a target. + */ + public ReturnaddressType(final InstructionHandle returnTarget) { + super(Const.T_ADDRESS, ""); + this.returnTarget = returnTarget; + } + + /** + * Returns if the two Returnaddresses refer to the same target. + */ + @Override + public boolean equals(final Object rat) { + if (!(rat instanceof ReturnaddressType)) { + return false; + } + final ReturnaddressType that = (ReturnaddressType) rat; + if (this.returnTarget == null || that.returnTarget == null) { + return that.returnTarget == this.returnTarget; + } + return that.returnTarget.equals(this.returnTarget); + } + + /** + * @return the target of this ReturnaddressType + */ + public InstructionHandle getTarget() { + return returnTarget; + } + + /** + * @return a hash code value for the object. + */ + @Override + public int hashCode() { + if (returnTarget == null) { + return 0; + } + return returnTarget.hashCode(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/SALOAD.java b/src/main/java/haidnor/jvm/bcel/generic/SALOAD.java new file mode 100644 index 0000000..eb89ecf --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/SALOAD.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * SALOAD - Load short from array + * + *
+ * Stack: ..., arrayref, index -> ..., value
+ * 
+ */ +public class SALOAD extends ArrayInstruction implements StackProducer { + + public SALOAD() { + super(Const.SALOAD); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackProducer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitSALOAD(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/SASTORE.java b/src/main/java/haidnor/jvm/bcel/generic/SASTORE.java new file mode 100644 index 0000000..a9444ad --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/SASTORE.java @@ -0,0 +1,48 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * SASTORE - Store into short array + * + *
+ * Stack: ..., arrayref, index, value -> ...
+ * 
+ */ +public class SASTORE extends ArrayInstruction implements StackConsumer { + + public SASTORE() { + super(Const.SASTORE); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitExceptionThrower(this); + v.visitTypedInstruction(this); + v.visitArrayInstruction(this); + v.visitSASTORE(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/SIPUSH.java b/src/main/java/haidnor/jvm/bcel/generic/SIPUSH.java new file mode 100644 index 0000000..b15e1b4 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/SIPUSH.java @@ -0,0 +1,100 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * SIPUSH - Push short + * + *
+ * Stack: ... -> ..., value
+ * 
+ */ +public class SIPUSH extends Instruction implements ConstantPushInstruction { + + private short b; + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + SIPUSH() { + } + + public SIPUSH(final short b) { + super(Const.SIPUSH, (short) 3); + this.b = b; + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitPushInstruction(this); + v.visitStackProducer(this); + v.visitTypedInstruction(this); + v.visitConstantPushInstruction(this); + v.visitSIPUSH(this); + } + + /** + * Dump instruction as short code to stream out. + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + super.dump(out); + out.writeShort(b); + } + + /** + * @return Type.SHORT + */ + @Override + public Type getType(final ConstantPoolGen cp) { + return Type.SHORT; + } + + @Override + public Number getValue() { + return Integer.valueOf(b); + } + + /** + * Read needed data (e.g. index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + super.setLength(3); + b = bytes.readShort(); + } + + /** + * @return mnemonic for instruction + */ + @Override + public String toString(final boolean verbose) { + return super.toString(verbose) + " " + b; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/SWAP.java b/src/main/java/haidnor/jvm/bcel/generic/SWAP.java new file mode 100644 index 0000000..7cca1cd --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/SWAP.java @@ -0,0 +1,47 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; + +/** + * SWAP - Swa top operand stack word + * + *
+ * Stack: ..., word2, word1 -> ..., word1, word2
+ * 
+ */ +public class SWAP extends StackInstruction implements StackConsumer, StackProducer { + + public SWAP() { + super(Const.SWAP); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitStackProducer(this); + v.visitStackInstruction(this); + v.visitSWAP(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/SWITCH.java b/src/main/java/haidnor/jvm/bcel/generic/SWITCH.java new file mode 100644 index 0000000..11fce8d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/SWITCH.java @@ -0,0 +1,134 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import java.util.Arrays; + +/** + * SWITCH - Branch depending on int value, generates either LOOKUPSWITCH or TABLESWITCH instruction, depending on + * whether the match values (int[]) can be sorted with no gaps between the numbers. + */ +public final class SWITCH implements CompoundInstruction { + + /** + * @return match is sorted in ascending order with no gap bigger than maxGap? + */ + private static boolean matchIsOrdered(final int[] match, final int matchLength, final int maxGap) { + for (int i = 1; i < matchLength; i++) { + if (match[i] - match[i - 1] > maxGap) { + return false; + } + } + return true; + } + + /** + * Sorts match and targets array with QuickSort. + */ + private static void sort(final int l, final int r, final int[] match, final InstructionHandle[] targets) { + int i = l; + int j = r; + int h; + final int m = match[l + r >>> 1]; + InstructionHandle h2; + do { + while (match[i] < m) { + i++; + } + while (m < match[j]) { + j--; + } + if (i <= j) { + h = match[i]; + match[i] = match[j]; + match[j] = h; // Swap elements + h2 = targets[i]; + targets[i] = targets[j]; + targets[j] = h2; // Swap instructions, too + i++; + j--; + } + } while (i <= j); + if (l < j) { + sort(l, j, match, targets); + } + if (i < r) { + sort(i, r, match, targets); + } + } + + private final Select instruction; + + public SWITCH(final int[] match, final InstructionHandle[] targets, final InstructionHandle target) { + this(match, targets, target, 1); + } + + /** + * Template for switch() constructs. If the match array can be sorted in ascending order with gaps no larger than + * maxGap between the numbers, a TABLESWITCH instruction is generated, and a LOOKUPSWITCH otherwise. The former may be + * more efficient, but needs more space. + * + * Note, that the key array always will be sorted, though we leave the original arrays unaltered. + * + * @param match array of match values (case 2: ... case 7: ..., etc.) + * @param targets the instructions to be branched to for each case + * @param target the default target + * @param maxGap maximum gap that may between case branches + */ + public SWITCH(final int[] match, final InstructionHandle[] targets, final InstructionHandle target, final int maxGap) { + int[] matchClone = match.clone(); + final InstructionHandle[] targetsClone = targets.clone(); + final int matchLength = match.length; + if (matchLength < 2) { + instruction = new TABLESWITCH(match, targets, target); + } else { + sort(0, matchLength - 1, matchClone, targetsClone); + if (matchIsOrdered(matchClone, matchLength, maxGap)) { + final int maxSize = matchLength + matchLength * maxGap; + final int[] mVec = new int[maxSize]; + final InstructionHandle[] tVec = new InstructionHandle[maxSize]; + int count = 1; + mVec[0] = match[0]; + tVec[0] = targets[0]; + for (int i = 1; i < matchLength; i++) { + final int prev = match[i - 1]; + final int gap = match[i] - prev; + for (int j = 1; j < gap; j++) { + mVec[count] = prev + j; + tVec[count] = target; + count++; + } + mVec[count] = match[i]; + tVec[count] = targets[i]; + count++; + } + instruction = new TABLESWITCH(Arrays.copyOf(mVec, count), Arrays.copyOf(tVec, count), target); + } else { + instruction = new LOOKUPSWITCH(matchClone, targetsClone, target); + } + } + } + + public Instruction getInstruction() { + return instruction; + } + + @Override + public InstructionList getInstructionList() { + return new InstructionList(instruction); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/Select.java b/src/main/java/haidnor/jvm/bcel/generic/Select.java new file mode 100644 index 0000000..3f1808a --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/Select.java @@ -0,0 +1,368 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Select - Abstract super class for LOOKUPSWITCH and TABLESWITCH instructions. + * + *

+ * We use our super's {@code target} property as the default target. + * + * @see LOOKUPSWITCH + * @see TABLESWITCH + * @see InstructionList + */ +public abstract class Select extends BranchInstruction implements VariableLengthInstruction, StackConsumer /* @since 6.0 */, StackProducer { + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected int[] match; // matches, i.e., case 1: ... TODO could be package-protected? + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected int[] indices; // target offsets TODO could be package-protected? + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected InstructionHandle[] targets; // target objects in instruction list TODO could be package-protected? + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected int fixed_length; // fixed length defined by subclasses TODO could be package-protected? + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected int match_length; // number of cases TODO could be package-protected? + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected int padding; // number of pad bytes for alignment TODO could be package-protected? + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + Select() { + } + + /** + * (Match, target) pairs for switch. 'Match' and 'targets' must have the same length of course. + * + * @param match array of matching values + * @param targets instruction targets + * @param defaultTarget default instruction target + */ + Select(final short opcode, final int[] match, final InstructionHandle[] targets, final InstructionHandle defaultTarget) { + // don't set default target before instuction is built + super(opcode, null); + this.match = match; + this.targets = targets; + // now it's safe to set default target + setTarget(defaultTarget); + for (final InstructionHandle target2 : targets) { + notifyTarget(null, target2, this); + } + if ((match_length = match.length) != targets.length) { + throw new ClassGenException("Match and target array have not the same length: Match length: " + match.length + " Target length: " + targets.length); + } + indices = new int[match_length]; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + final Select copy = (Select) super.clone(); + copy.match = match.clone(); + copy.indices = indices.clone(); + copy.targets = targets.clone(); + return copy; + } + + /** + * @return true, if ih is target of this instruction + */ + @Override + public boolean containsTarget(final InstructionHandle ih) { + if (super.getTarget() == ih) { + return true; + } + for (final InstructionHandle target2 : targets) { + if (target2 == ih) { + return true; + } + } + return false; + } + + /** + * Inform targets that they're not targeted anymore. + */ + @Override + void dispose() { + super.dispose(); + for (final InstructionHandle target2 : targets) { + target2.removeTargeter(this); + } + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + out.writeByte(super.getOpcode()); + for (int i = 0; i < padding; i++) { + out.writeByte(0); + } + super.setIndex(getTargetOffset()); // Write default target offset + out.writeInt(super.getIndex()); + } + + /** + * @return the fixed_length + * @since 6.0 + */ + final int getFixedLength() { + return fixed_length; + } + + /** + * @return array of match target offsets + */ + public int[] getIndices() { + return indices; + } + + /** + * @return index entry from indices + * @since 6.0 + */ + final int getIndices(final int index) { + return indices[index]; + } + + /** + * @return match entry + * @since 6.0 + */ + final int getMatch(final int index) { + return match[index]; + } + + /** + * @return the match_length + * @since 6.0 + */ + final int getMatchLength() { + return match_length; + } + + /** + * @return array of match indices + */ + public int[] getMatchs() { + return match; + } + + /** + * + * @return the padding + * @since 6.0 + */ + final int getPadding() { + return padding; + } + + /** + * @return target entry + * @since 6.0 + */ + final InstructionHandle getTarget(final int index) { + return targets[index]; + } + + /** + * @return array of match targets + */ + public InstructionHandle[] getTargets() { + return targets; + } + + /** + * Read needed data (e.g. index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + padding = (4 - bytes.getIndex() % 4) % 4; // Compute number of pad bytes + for (int i = 0; i < padding; i++) { + bytes.readByte(); + } + // Default branch target common for both cases (TABLESWITCH, LOOKUPSWITCH) + super.setIndex(bytes.readInt()); + } + + /** + * @param fixedLength the fixed_length to set + * @since 6.0 + */ + final void setFixedLength(final int fixedLength) { + this.fixed_length = fixedLength; + } + + /** @since 6.0 */ + final int setIndices(final int i, final int value) { + indices[i] = value; + return value; // Allow use in nested calls + } + + /** + * + * @param array + * @since 6.0 + */ + final void setIndices(final int[] array) { + indices = array; + } + + /** + * + * @param index + * @param value + * @since 6.0 + */ + final void setMatch(final int index, final int value) { + match[index] = value; + } + + /** + * + * @param array + * @since 6.0 + */ + final void setMatches(final int[] array) { + match = array; + } + + /** + * @param matchLength the match_length to set + * @since 6.0 + */ + final int setMatchLength(final int matchLength) { + this.match_length = matchLength; + return matchLength; + } + + /** + * Set branch target for 'i'th case + */ + public void setTarget(final int i, final InstructionHandle target) { // TODO could be package-protected? + notifyTarget(targets[i], target, this); + targets[i] = target; + } + + /** + * + * @param array + * @since 6.0 + */ + final void setTargets(final InstructionHandle[] array) { + targets = array; + } + + /** + * @return mnemonic for instruction + */ + @Override + public String toString(final boolean verbose) { + final StringBuilder buf = new StringBuilder(super.toString(verbose)); + if (verbose) { + for (int i = 0; i < match_length; i++) { + String s = "null"; + if (targets[i] != null) { + s = targets[i].getInstruction().toString(); + } + buf.append("(").append(match[i]).append(", ").append(s).append(" = {").append(indices[i]).append("})"); + } + } else { + buf.append(" ..."); + } + return buf.toString(); + } + + /** + * Since this is a variable length instruction, it may shift the following instructions which then need to update their + * position. + * + * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable + * length instructions 'setPositions' performs multiple passes over the instruction list to calculate the correct (byte) + * positions and offsets by calling this function. + * + * @param offset additional offset caused by preceding (variable length) instructions + * @param maxOffset the maximum offset that may be caused by these instructions + * @return additional offset caused by possible change of this instruction's length + */ + @Override + protected int updatePosition(final int offset, final int maxOffset) { + setPosition(getPosition() + offset); // Additional offset caused by preceding SWITCHs, GOTOs, etc. + final short oldLength = (short) super.getLength(); + /* + * Alignment on 4-byte-boundary, + 1, because of tag byte. + */ + padding = (4 - (getPosition() + 1) % 4) % 4; + super.setLength((short) (fixed_length + padding)); // Update length + return super.getLength() - oldLength; + } + + /** + * @param oldIh old target + * @param newIh new target + */ + @Override + public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) { + boolean targeted = false; + if (super.getTarget() == oldIh) { + targeted = true; + setTarget(newIh); + } + for (int i = 0; i < targets.length; i++) { + if (targets[i] == oldIh) { + targeted = true; + setTarget(i, newIh); + } + } + if (!targeted) { + throw new ClassGenException("Not targeting " + oldIh); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/SimpleElementValueGen.java b/src/main/java/haidnor/jvm/bcel/generic/SimpleElementValueGen.java new file mode 100644 index 0000000..4b53711 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/SimpleElementValueGen.java @@ -0,0 +1,228 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.classfile.*; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * @since 6.0 + */ +public class SimpleElementValueGen extends ElementValueGen { + // For primitive types and string type, this points to the value entry in + // the cpGen + // For 'class' this points to the class entry in the cpGen + private final int idx; + + public SimpleElementValueGen(final int type, final ConstantPoolGen cpGen, final boolean value) { + super(type, cpGen); + if (value) { + idx = getConstantPool().addInteger(1); + } else { + idx = getConstantPool().addInteger(0); + } + } + + public SimpleElementValueGen(final int type, final ConstantPoolGen cpGen, final byte value) { + super(type, cpGen); + idx = getConstantPool().addInteger(value); + } + + public SimpleElementValueGen(final int type, final ConstantPoolGen cpGen, final char value) { + super(type, cpGen); + idx = getConstantPool().addInteger(value); + } + + public SimpleElementValueGen(final int type, final ConstantPoolGen cpGen, final double value) { + super(type, cpGen); + idx = getConstantPool().addDouble(value); + } + + public SimpleElementValueGen(final int type, final ConstantPoolGen cpGen, final float value) { + super(type, cpGen); + idx = getConstantPool().addFloat(value); + } + + public SimpleElementValueGen(final int type, final ConstantPoolGen cpGen, final int value) { + super(type, cpGen); + idx = getConstantPool().addInteger(value); + } + + public SimpleElementValueGen(final int type, final ConstantPoolGen cpGen, final long value) { + super(type, cpGen); + idx = getConstantPool().addLong(value); + } + + public SimpleElementValueGen(final int type, final ConstantPoolGen cpGen, final short value) { + super(type, cpGen); + idx = getConstantPool().addInteger(value); + } + + public SimpleElementValueGen(final int type, final ConstantPoolGen cpGen, final String value) { + super(type, cpGen); + idx = getConstantPool().addUtf8(value); + } + + // ctors for each supported type... type could be inferred but for now lets + // force it to be passed + /** + * Protected ctor used for deserialization, doesn't *put* an entry in the constant pool, assumes the one at the supplied + * index is correct. + */ + protected SimpleElementValueGen(final int type, final int idx, final ConstantPoolGen cpGen) { + super(type, cpGen); + this.idx = idx; + } + + /** + * The boolean controls whether we copy info from the 'old' constant pool to the 'new'. You need to use this ctor if the + * annotation is being copied from one file to another. + */ + public SimpleElementValueGen(final SimpleElementValue value, final ConstantPoolGen cpool, final boolean copyPoolEntries) { + super(value.getElementValueType(), cpool); + if (!copyPoolEntries) { + // J5ASSERT: Could assert value.stringifyValue() is the same as + // cpool.getConstant(SimpleElementValuevalue.getIndex()) + idx = value.getIndex(); + } else { + switch (value.getElementValueType()) { + case STRING: + idx = cpool.addUtf8(value.getValueString()); + break; + case PRIMITIVE_INT: + idx = cpool.addInteger(value.getValueInt()); + break; + case PRIMITIVE_BYTE: + idx = cpool.addInteger(value.getValueByte()); + break; + case PRIMITIVE_CHAR: + idx = cpool.addInteger(value.getValueChar()); + break; + case PRIMITIVE_LONG: + idx = cpool.addLong(value.getValueLong()); + break; + case PRIMITIVE_FLOAT: + idx = cpool.addFloat(value.getValueFloat()); + break; + case PRIMITIVE_DOUBLE: + idx = cpool.addDouble(value.getValueDouble()); + break; + case PRIMITIVE_BOOLEAN: + if (value.getValueBoolean()) { + idx = cpool.addInteger(1); + } else { + idx = cpool.addInteger(0); + } + break; + case PRIMITIVE_SHORT: + idx = cpool.addInteger(value.getValueShort()); + break; + default: + throw new IllegalArgumentException("SimpleElementValueGen class does not know how to copy this type " + super.getElementValueType()); + } + } + } + + @Override + public void dump(final DataOutputStream dos) throws IOException { + dos.writeByte(super.getElementValueType()); // u1 kind of value + switch (super.getElementValueType()) { + case PRIMITIVE_INT: + case PRIMITIVE_BYTE: + case PRIMITIVE_CHAR: + case PRIMITIVE_FLOAT: + case PRIMITIVE_LONG: + case PRIMITIVE_BOOLEAN: + case PRIMITIVE_SHORT: + case PRIMITIVE_DOUBLE: + case STRING: + dos.writeShort(idx); + break; + default: + throw new IllegalStateException("SimpleElementValueGen doesnt know how to write out type " + super.getElementValueType()); + } + } + + /** + * Return immutable variant + */ + @Override + public ElementValue getElementValue() { + return new SimpleElementValue(super.getElementValueType(), idx, getConstantPool().getConstantPool()); + } + + public int getIndex() { + return idx; + } + + public int getValueInt() { + if (super.getElementValueType() != PRIMITIVE_INT) { + throw new IllegalStateException("Dont call getValueString() on a non STRING ElementValue"); + } + final ConstantInteger c = (ConstantInteger) getConstantPool().getConstant(idx); + return c.getBytes(); + } + + public String getValueString() { + if (super.getElementValueType() != STRING) { + throw new IllegalStateException("Dont call getValueString() on a non STRING ElementValue"); + } + final ConstantUtf8 c = (ConstantUtf8) getConstantPool().getConstant(idx); + return c.getBytes(); + } + + // Whatever kind of value it is, return it as a string + @Override + public String stringifyValue() { + switch (super.getElementValueType()) { + case PRIMITIVE_INT: + final ConstantInteger c = (ConstantInteger) getConstantPool().getConstant(idx); + return Integer.toString(c.getBytes()); + case PRIMITIVE_LONG: + final ConstantLong j = (ConstantLong) getConstantPool().getConstant(idx); + return Long.toString(j.getBytes()); + case PRIMITIVE_DOUBLE: + final ConstantDouble d = (ConstantDouble) getConstantPool().getConstant(idx); + return Double.toString(d.getBytes()); + case PRIMITIVE_FLOAT: + final ConstantFloat f = (ConstantFloat) getConstantPool().getConstant(idx); + return Float.toString(f.getBytes()); + case PRIMITIVE_SHORT: + final ConstantInteger s = (ConstantInteger) getConstantPool().getConstant(idx); + return Integer.toString(s.getBytes()); + case PRIMITIVE_BYTE: + final ConstantInteger b = (ConstantInteger) getConstantPool().getConstant(idx); + return Integer.toString(b.getBytes()); + case PRIMITIVE_CHAR: + final ConstantInteger ch = (ConstantInteger) getConstantPool().getConstant(idx); + return Integer.toString(ch.getBytes()); + case PRIMITIVE_BOOLEAN: + final ConstantInteger bo = (ConstantInteger) getConstantPool().getConstant(idx); + if (bo.getBytes() == 0) { + return "false"; + } + return "true"; + case STRING: + final ConstantUtf8 cu8 = (ConstantUtf8) getConstantPool().getConstant(idx); + return cu8.getBytes(); + default: + throw new IllegalStateException("SimpleElementValueGen class does not know how to stringify type " + super.getElementValueType()); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/StackConsumer.java b/src/main/java/haidnor/jvm/bcel/generic/StackConsumer.java new file mode 100644 index 0000000..ff57a56 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/StackConsumer.java @@ -0,0 +1,28 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denote an instruction that may consume a value from the stack. + */ +public interface StackConsumer { + + /** + * @return how many words are consumed from stack + */ + int consumeStack(ConstantPoolGen cpg); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/StackInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/StackInstruction.java new file mode 100644 index 0000000..10848a6 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/StackInstruction.java @@ -0,0 +1,43 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Super class for stack operations like DUP and POP. + */ +public abstract class StackInstruction extends Instruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + StackInstruction() { + } + + /** + * @param opcode instruction opcode + */ + protected StackInstruction(final short opcode) { + super(opcode, (short) 1); + } + + /** + * @return Type.UNKNOWN + */ + public Type getType(final ConstantPoolGen cp) { + return Type.UNKNOWN; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/StackProducer.java b/src/main/java/haidnor/jvm/bcel/generic/StackProducer.java new file mode 100644 index 0000000..d74d9e9 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/StackProducer.java @@ -0,0 +1,28 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denotes an instruction that may produce a value on top of the stack (this excludes DUP_X1, e.g.) + */ +public interface StackProducer { + + /** + * @return how many words are produced on stack + */ + int produceStack(ConstantPoolGen cpg); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/StoreInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/StoreInstruction.java new file mode 100644 index 0000000..77b6bd9 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/StoreInstruction.java @@ -0,0 +1,55 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denotes an unparameterized instruction to store a value into a local variable, e.g. ISTORE. + */ +public abstract class StoreInstruction extends LocalVariableInstruction implements PopInstruction { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. tag and length are defined in + * readInstruction and initFromFile, respectively. + */ + StoreInstruction(final short canonTag, final short cTag) { + super(canonTag, cTag); + } + + /** + * @param opcode Instruction opcode + * @param cTag Instruction number for compact version, ASTORE_0, e.g. + * @param n local variable index (unsigned short) + */ + protected StoreInstruction(final short opcode, final short cTag, final int n) { + super(opcode, cTag, n); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitStackConsumer(this); + v.visitPopInstruction(this); + v.visitTypedInstruction(this); + v.visitLocalVariableInstruction(this); + v.visitStoreInstruction(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/TABLESWITCH.java b/src/main/java/haidnor/jvm/bcel/generic/TABLESWITCH.java new file mode 100644 index 0000000..583fa1c --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/TABLESWITCH.java @@ -0,0 +1,105 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.util.ByteSequence; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * TABLESWITCH - Switch within given range of values, i.e., low..high + * + * @see SWITCH + */ +public class TABLESWITCH extends Select { + + /** + * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. + */ + TABLESWITCH() { + } + + /** + * @param match sorted array of match values, match[0] must be low value, match[match_length - 1] high value + * @param targets where to branch for matched values + * @param defaultTarget default branch + */ + public TABLESWITCH(final int[] match, final InstructionHandle[] targets, final InstructionHandle defaultTarget) { + super(Const.TABLESWITCH, match, targets, defaultTarget); + /* Alignment remainder assumed 0 here, until dump time */ + final short length = (short) (13 + getMatchLength() * 4); + super.setLength(length); + setFixedLength(length); + } + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call + * methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitVariableLengthInstruction(this); + v.visitStackConsumer(this); + v.visitBranchInstruction(this); + v.visitSelect(this); + v.visitTABLESWITCH(this); + } + + /** + * Dump instruction as byte code to stream out. + * + * @param out Output stream + */ + @Override + public void dump(final DataOutputStream out) throws IOException { + super.dump(out); + final int matchLength = getMatchLength(); + final int low = matchLength > 0 ? super.getMatch(0) : 0; + out.writeInt(low); + final int high = matchLength > 0 ? super.getMatch(matchLength - 1) : 0; + out.writeInt(high); + for (int i = 0; i < matchLength; i++) { + out.writeInt(setIndices(i, getTargetOffset(super.getTarget(i)))); + } + } + + /** + * Read needed data (e.g. index) from file. + */ + @Override + protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { + super.initFromFile(bytes, wide); + final int low = bytes.readInt(); + final int high = bytes.readInt(); + final int matchLength = high - low + 1; + setMatchLength(matchLength); + final short fixedLength = (short) (13 + matchLength * 4); + setFixedLength(fixedLength); + super.setLength((short) (fixedLength + super.getPadding())); + super.setMatches(new int[matchLength]); + super.setIndices(new int[matchLength]); + super.setTargets(new InstructionHandle[matchLength]); + for (int i = 0; i < matchLength; i++) { + super.setMatch(i, low + i); + super.setIndices(i, bytes.readInt()); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/TargetLostException.java b/src/main/java/haidnor/jvm/bcel/generic/TargetLostException.java new file mode 100644 index 0000000..c2ac5fd --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/TargetLostException.java @@ -0,0 +1,62 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Thrown by InstructionList.remove() when one or multiple disposed instructions are still being referenced by an + * InstructionTargeter object. I.e. the InstructionTargeter has to be notified that (one of) the InstructionHandle it is + * referencing is being removed from the InstructionList and thus not valid anymore. + * + *

+ * Making this an exception instead of a return value forces the user to handle these case explicitly in a try { ... } + * catch. The following code illustrates how this may be done: + *

+ * + *
+ *     ...
+ *     try {
+ *         il.delete(start_ih, end_ih);
+ *     } catch(TargetLostException e) {
+ *         for (InstructionHandle target : e.getTargets()) {
+ *             for (InstructionTargeter targeter : target.getTargeters()) {
+ *                 targeter.updateTarget(target, new_target);
+ *             }
+ *         }
+ *     }
+ * 
+ * + * @see InstructionHandle + * @see InstructionList + * @see InstructionTargeter + */ +public final class TargetLostException extends Exception { + + private static final long serialVersionUID = -6857272667645328384L; + private final InstructionHandle[] targets; + + TargetLostException(final InstructionHandle[] t, final String mesg) { + super(mesg); + targets = t; + } + + /** + * @return list of instructions still being targeted. + */ + public InstructionHandle[] getTargets() { + return targets; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/Type.java b/src/main/java/haidnor/jvm/bcel/generic/Type.java new file mode 100644 index 0000000..8851d51 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/Type.java @@ -0,0 +1,395 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.ClassFormatException; +import haidnor.jvm.bcel.classfile.Utility; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * Abstract super class for all possible java types, namely basic types such as int, object types like String and array + * types, e.g. int[] + */ +public abstract class Type { + + /** + * Predefined constants + */ + public static final BasicType VOID = new BasicType(Const.T_VOID); + + public static final BasicType BOOLEAN = new BasicType(Const.T_BOOLEAN); + public static final BasicType INT = new BasicType(Const.T_INT); + public static final BasicType SHORT = new BasicType(Const.T_SHORT); + public static final BasicType BYTE = new BasicType(Const.T_BYTE); + public static final BasicType LONG = new BasicType(Const.T_LONG); + public static final BasicType DOUBLE = new BasicType(Const.T_DOUBLE); + public static final BasicType FLOAT = new BasicType(Const.T_FLOAT); + public static final BasicType CHAR = new BasicType(Const.T_CHAR); + public static final ObjectType OBJECT = new ObjectType("java.lang.Object"); + public static final ObjectType CLASS = new ObjectType("java.lang.Class"); + public static final ObjectType STRING = new ObjectType("java.lang.String"); + public static final ObjectType STRINGBUFFER = new ObjectType("java.lang.StringBuffer"); + public static final ObjectType THROWABLE = new ObjectType("java.lang.Throwable"); + + /** + * Empty array. + */ + public static final Type[] NO_ARGS = {}; + public static final ReferenceType NULL = new ReferenceType() { + }; + + public static final Type UNKNOWN = new Type(Const.T_UNKNOWN, "") { + }; + + private static final ThreadLocal CONSUMED_CHARS = ThreadLocal.withInitial(() -> Integer.valueOf(0)); + + // int consumed_chars=0; // Remember position in string, see getArgumentTypes + static int consumed(final int coded) { + return coded >> 2; + } + + static int encode(final int size, final int consumed) { + return consumed << 2 | size; + } + + /** + * Convert arguments of a method (signature) to an array of Type objects. + * + * @param signature signature string such as (Ljava/lang/String;)V + * @return array of argument types + */ + public static Type[] getArgumentTypes(final String signature) { + final List vec = new ArrayList<>(); + int index; + try { + // Skip any type arguments to read argument declarations between '(' and ')' + index = signature.indexOf('(') + 1; + if (index <= 0) { + throw new ClassFormatException("Invalid method signature: " + signature); + } + while (signature.charAt(index) != ')') { + vec.add(getType(signature.substring(index))); + // corrected concurrent private static field acess + index += unwrap(CONSUMED_CHARS); // update position + } + } catch (final StringIndexOutOfBoundsException e) { // Should never occur + throw new ClassFormatException("Invalid method signature: " + signature, e); + } + final Type[] types = new Type[vec.size()]; + vec.toArray(types); + return types; + } + + static int getArgumentTypesSize(final String signature) { + int res = 0; + int index; + try { + // Skip any type arguments to read argument declarations between '(' and ')' + index = signature.indexOf('(') + 1; + if (index <= 0) { + throw new ClassFormatException("Invalid method signature: " + signature); + } + while (signature.charAt(index) != ')') { + final int coded = getTypeSize(signature.substring(index)); + res += size(coded); + index += consumed(coded); + } + } catch (final StringIndexOutOfBoundsException e) { // Should never occur + throw new ClassFormatException("Invalid method signature: " + signature, e); + } + return res; + } + + /** + * Convert type to Java method signature, e.g. int[] f(java.lang.String x) becomes (Ljava/lang/String;)[I + * + * @param returnType what the method returns + * @param argTypes what are the argument types + * @return method signature for given type(s). + */ + public static String getMethodSignature(final Type returnType, final Type[] argTypes) { + final StringBuilder buf = new StringBuilder("("); + if (argTypes != null) { + for (final Type argType : argTypes) { + buf.append(argType.getSignature()); + } + } + buf.append(')'); + buf.append(returnType.getSignature()); + return buf.toString(); + } + + /** + * Convert return value of a method (signature) to a Type object. + * + * @param signature signature string such as (Ljava/lang/String;)V + * @return return type + */ + public static Type getReturnType(final String signature) { + try { + // Read return type after ')' + final int index = signature.lastIndexOf(')') + 1; + return getType(signature.substring(index)); + } catch (final StringIndexOutOfBoundsException e) { // Should never occur + throw new ClassFormatException("Invalid method signature: " + signature, e); + } + } + + static int getReturnTypeSize(final String signature) { + final int index = signature.lastIndexOf(')') + 1; + return Type.size(getTypeSize(signature.substring(index))); + } + + public static String getSignature(final java.lang.reflect.Method meth) { + final StringBuilder sb = new StringBuilder("("); + final Class[] params = meth.getParameterTypes(); // avoid clone + for (final Class param : params) { + sb.append(getType(param).getSignature()); + } + sb.append(")"); + sb.append(getType(meth.getReturnType()).getSignature()); + return sb.toString(); + } + + /** + * Convert runtime java.lang.Class to BCEL Type object. + * + * @param cls Java class + * @return corresponding Type object + */ + public static Type getType(final Class cls) { + Objects.requireNonNull(cls, "cls"); + /* + * That's an amzingly easy case, because getName() returns the signature. That's what we would have liked anyway. + */ + if (cls.isArray()) { + return getType(cls.getName()); + } + if (!cls.isPrimitive()) { // "Real" class + return ObjectType.getInstance(cls.getName()); + } + if (cls == Integer.TYPE) { + return INT; + } + if (cls == Void.TYPE) { + return VOID; + } + if (cls == Double.TYPE) { + return DOUBLE; + } + if (cls == Float.TYPE) { + return FLOAT; + } + if (cls == Boolean.TYPE) { + return BOOLEAN; + } + if (cls == Byte.TYPE) { + return BYTE; + } + if (cls == Short.TYPE) { + return SHORT; + } + if (cls == Long.TYPE) { + return LONG; + } + if (cls == Character.TYPE) { + return CHAR; + } + throw new IllegalStateException("Unknown primitive type " + cls); + } + + /** + * Convert signature to a Type object. + * + * @param signature signature string such as Ljava/lang/String; + * @return type object + */ + public static Type getType(final String signature) throws StringIndexOutOfBoundsException { + final byte type = Utility.typeOfSignature(signature); + if (type <= Const.T_VOID) { + // corrected concurrent private static field acess + wrap(CONSUMED_CHARS, 1); + return BasicType.getType(type); + } + if (type != Const.T_ARRAY) { // type == T_REFERENCE + // Utility.typeSignatureToString understands how to parse generic types. + final String parsedSignature = Utility.typeSignatureToString(signature, false); + wrap(CONSUMED_CHARS, parsedSignature.length() + 2); // "Lblabla;" 'L' and ';' are removed + return ObjectType.getInstance(Utility.pathToPackage(parsedSignature)); + } + int dim = 0; + do { // Count dimensions + dim++; + } while (signature.charAt(dim) == '['); + // Recurse, but just once, if the signature is ok + final Type t = getType(signature.substring(dim)); + // corrected concurrent private static field acess + // consumed_chars += dim; // update counter - is replaced by + final int temp = unwrap(CONSUMED_CHARS) + dim; + wrap(CONSUMED_CHARS, temp); + return new ArrayType(t, dim); + } + + /** + * Convert runtime java.lang.Class[] to BCEL Type objects. + * + * @param classes an array of runtime class objects + * @return array of corresponding Type objects + */ + public static Type[] getTypes(final Class[] classes) { + final Type[] ret = new Type[classes.length]; + Arrays.setAll(ret, i -> getType(classes[i])); + return ret; + } + + static int getTypeSize(final String signature) throws StringIndexOutOfBoundsException { + final byte type = Utility.typeOfSignature(signature); + if (type <= Const.T_VOID) { + return encode(BasicType.getType(type).getSize(), 1); + } + if (type == Const.T_ARRAY) { + int dim = 0; + do { // Count dimensions + dim++; + } while (signature.charAt(dim) == '['); + // Recurse, but just once, if the signature is ok + final int consumed = consumed(getTypeSize(signature.substring(dim))); + return encode(1, dim + consumed); + } + final int index = signature.indexOf(';'); // Look for closing ';' + if (index < 0) { + throw new ClassFormatException("Invalid signature: " + signature); + } + return encode(1, index + 1); + } + + static int size(final int coded) { + return coded & 3; + } + + private static int unwrap(final ThreadLocal tl) { + return tl.get().intValue(); + } + + private static void wrap(final ThreadLocal tl, final int value) { + tl.set(Integer.valueOf(value)); + } + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected byte type; // TODO should be final (and private) + + /** + * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter + */ + @Deprecated + protected String signature; // signature for the type TODO should be private + + protected Type(final byte type, final String signature) { + this.type = type; + this.signature = signature; + } + + /** + * @return whether the Types are equal + */ + @Override + public boolean equals(final Object o) { + if (o instanceof Type) { + final Type t = (Type) o; + return type == t.type && signature.equals(t.signature); + } + return false; + } + + public String getClassName() { + return toString(); + } + + /** + * @return signature for given type. + */ + public String getSignature() { + return signature; + } + + /** + * @return stack size of this type (2 for long and double, 0 for void, 1 otherwise) + */ + public int getSize() { + switch (type) { + case Const.T_DOUBLE: + case Const.T_LONG: + return 2; + case Const.T_VOID: + return 0; + default: + return 1; + } + } + + /** + * @return type as defined in Constants + */ + public byte getType() { + return type; + } + + /** + * @return hashcode of Type + */ + @Override + public int hashCode() { + return type ^ signature.hashCode(); + } + + /** + * boolean, short and char variable are considered as int in the stack or local variable area. Returns {@link Type#INT} + * for {@link Type#BOOLEAN}, {@link Type#SHORT} or {@link Type#CHAR}, otherwise returns the given type. + * + * @since 6.0 + */ + public Type normalizeForStackOrLocal() { + if (this == Type.BOOLEAN || this == Type.BYTE || this == Type.SHORT || this == Type.CHAR) { + return Type.INT; + } + return this; + } + + /* + * Currently only used by the ArrayType constructor. The signature has a complicated dependency on other parameter so + * it's tricky to do it in a call to the super ctor. + */ + void setSignature(final String signature) { + this.signature = signature; + } + + /** + * @return Type string, e.g. 'int[]' + */ + @Override + public String toString() { + return this.equals(Type.NULL) || type >= Const.T_UNKNOWN ? signature : Utility.signatureToString(signature, false); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/TypedInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/TypedInstruction.java new file mode 100644 index 0000000..51a8a85 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/TypedInstruction.java @@ -0,0 +1,25 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Get the type associated with an instruction, int for ILOAD, or the type of the field of a PUTFIELD instruction, e.g.. + */ +public interface TypedInstruction { + + Type getType(ConstantPoolGen cpg); +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/UnconditionalBranch.java b/src/main/java/haidnor/jvm/bcel/generic/UnconditionalBranch.java new file mode 100644 index 0000000..0a97cc4 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/UnconditionalBranch.java @@ -0,0 +1,26 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denotes an instruction to perform an unconditional branch, i.e., GOTO, JSR. + * + * @see GOTO + * @see JSR + */ +public interface UnconditionalBranch { +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/VariableLengthInstruction.java b/src/main/java/haidnor/jvm/bcel/generic/VariableLengthInstruction.java new file mode 100644 index 0000000..af4feb4 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/VariableLengthInstruction.java @@ -0,0 +1,29 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Denotes an instruction to be a variable length instruction, such as GOTO, JSR, LOOKUPSWITCH and TABLESWITCH. + * + * + * @see GOTO + * @see JSR + * @see LOOKUPSWITCH + * @see TABLESWITCH + */ +public interface VariableLengthInstruction { +} diff --git a/src/main/java/haidnor/jvm/bcel/generic/Visitor.java b/src/main/java/haidnor/jvm/bcel/generic/Visitor.java new file mode 100644 index 0000000..db13d6e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/generic/Visitor.java @@ -0,0 +1,389 @@ +/* + * 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 haidnor.jvm.bcel.generic; + +/** + * Interface implementing the Visitor pattern programming style. I.e., a class that implements this interface can handle + * all types of instructions with the properly typed methods just by calling the accept() method. + */ +public interface Visitor { + + void visitAALOAD(AALOAD obj); + + void visitAASTORE(AASTORE obj); + + void visitACONST_NULL(ACONST_NULL obj); + + void visitAllocationInstruction(AllocationInstruction obj); + + void visitALOAD(ALOAD obj); + + void visitANEWARRAY(ANEWARRAY obj); + + void visitARETURN(ARETURN obj); + + void visitArithmeticInstruction(ArithmeticInstruction obj); + + void visitArrayInstruction(ArrayInstruction obj); + + void visitARRAYLENGTH(ARRAYLENGTH obj); + + void visitASTORE(ASTORE obj); + + void visitATHROW(ATHROW obj); + + void visitBALOAD(BALOAD obj); + + void visitBASTORE(BASTORE obj); + + void visitBIPUSH(BIPUSH obj); + + void visitBranchInstruction(BranchInstruction obj); + + void visitBREAKPOINT(BREAKPOINT obj); + + void visitCALOAD(CALOAD obj); + + void visitCASTORE(CASTORE obj); + + void visitCHECKCAST(CHECKCAST obj); + + void visitConstantPushInstruction(ConstantPushInstruction obj); + + void visitConversionInstruction(ConversionInstruction obj); + + void visitCPInstruction(CPInstruction obj); + + void visitD2F(D2F obj); + + void visitD2I(D2I obj); + + void visitD2L(D2L obj); + + void visitDADD(DADD obj); + + void visitDALOAD(DALOAD obj); + + void visitDASTORE(DASTORE obj); + + void visitDCMPG(DCMPG obj); + + void visitDCMPL(DCMPL obj); + + void visitDCONST(DCONST obj); + + void visitDDIV(DDIV obj); + + void visitDLOAD(DLOAD obj); + + void visitDMUL(DMUL obj); + + void visitDNEG(DNEG obj); + + void visitDREM(DREM obj); + + void visitDRETURN(DRETURN obj); + + void visitDSTORE(DSTORE obj); + + void visitDSUB(DSUB obj); + + void visitDUP(DUP obj); + + void visitDUP_X1(DUP_X1 obj); + + void visitDUP_X2(DUP_X2 obj); + + void visitDUP2(DUP2 obj); + + void visitDUP2_X1(DUP2_X1 obj); + + void visitDUP2_X2(DUP2_X2 obj); + + void visitExceptionThrower(ExceptionThrower obj); + + void visitF2D(F2D obj); + + void visitF2I(F2I obj); + + void visitF2L(F2L obj); + + void visitFADD(FADD obj); + + void visitFALOAD(FALOAD obj); + + void visitFASTORE(FASTORE obj); + + void visitFCMPG(FCMPG obj); + + void visitFCMPL(FCMPL obj); + + void visitFCONST(FCONST obj); + + void visitFDIV(FDIV obj); + + void visitFieldInstruction(FieldInstruction obj); + + void visitFieldOrMethod(FieldOrMethod obj); + + void visitFLOAD(FLOAD obj); + + void visitFMUL(FMUL obj); + + void visitFNEG(FNEG obj); + + void visitFREM(FREM obj); + + void visitFRETURN(FRETURN obj); + + void visitFSTORE(FSTORE obj); + + void visitFSUB(FSUB obj); + + void visitGETFIELD(GETFIELD obj); + + void visitGETSTATIC(GETSTATIC obj); + + void visitGOTO(GOTO obj); + + void visitGOTO_W(GOTO_W obj); + + void visitGotoInstruction(GotoInstruction obj); + + void visitI2B(I2B obj); + + void visitI2C(I2C obj); + + void visitI2D(I2D obj); + + void visitI2F(I2F obj); + + void visitI2L(I2L obj); + + void visitI2S(I2S obj); + + void visitIADD(IADD obj); + + void visitIALOAD(IALOAD obj); + + void visitIAND(IAND obj); + + void visitIASTORE(IASTORE obj); + + void visitICONST(ICONST obj); + + void visitIDIV(IDIV obj); + + void visitIF_ACMPEQ(IF_ACMPEQ obj); + + void visitIF_ACMPNE(IF_ACMPNE obj); + + void visitIF_ICMPEQ(IF_ICMPEQ obj); + + void visitIF_ICMPGE(IF_ICMPGE obj); + + void visitIF_ICMPGT(IF_ICMPGT obj); + + void visitIF_ICMPLE(IF_ICMPLE obj); + + void visitIF_ICMPLT(IF_ICMPLT obj); + + void visitIF_ICMPNE(IF_ICMPNE obj); + + void visitIFEQ(IFEQ obj); + + void visitIFGE(IFGE obj); + + void visitIFGT(IFGT obj); + + void visitIfInstruction(IfInstruction obj); + + void visitIFLE(IFLE obj); + + void visitIFLT(IFLT obj); + + void visitIFNE(IFNE obj); + + void visitIFNONNULL(IFNONNULL obj); + + void visitIFNULL(IFNULL obj); + + void visitIINC(IINC obj); + + void visitILOAD(ILOAD obj); + + void visitIMPDEP1(IMPDEP1 obj); + + void visitIMPDEP2(IMPDEP2 obj); + + void visitIMUL(IMUL obj); + + void visitINEG(INEG obj); + + void visitINSTANCEOF(INSTANCEOF obj); + + /** + * @since 6.0 + */ + void visitINVOKEDYNAMIC(INVOKEDYNAMIC obj); + + void visitInvokeInstruction(InvokeInstruction obj); + + void visitINVOKEINTERFACE(INVOKEINTERFACE obj); + + void visitINVOKESPECIAL(INVOKESPECIAL obj); + + void visitINVOKESTATIC(INVOKESTATIC obj); + + void visitINVOKEVIRTUAL(INVOKEVIRTUAL obj); + + void visitIOR(IOR obj); + + void visitIREM(IREM obj); + + void visitIRETURN(IRETURN obj); + + void visitISHL(ISHL obj); + + void visitISHR(ISHR obj); + + void visitISTORE(ISTORE obj); + + void visitISUB(ISUB obj); + + void visitIUSHR(IUSHR obj); + + void visitIXOR(IXOR obj); + + void visitJSR(JSR obj); + + void visitJSR_W(JSR_W obj); + + void visitJsrInstruction(JsrInstruction obj); + + void visitL2D(L2D obj); + + void visitL2F(L2F obj); + + void visitL2I(L2I obj); + + void visitLADD(LADD obj); + + void visitLALOAD(LALOAD obj); + + void visitLAND(LAND obj); + + void visitLASTORE(LASTORE obj); + + void visitLCMP(LCMP obj); + + void visitLCONST(LCONST obj); + + void visitLDC(LDC obj); + + void visitLDC2_W(LDC2_W obj); + + void visitLDIV(LDIV obj); + + void visitLLOAD(LLOAD obj); + + void visitLMUL(LMUL obj); + + void visitLNEG(LNEG obj); + + void visitLoadClass(LoadClass obj); + + void visitLoadInstruction(LoadInstruction obj); + + void visitLocalVariableInstruction(LocalVariableInstruction obj); + + void visitLOOKUPSWITCH(LOOKUPSWITCH obj); + + void visitLOR(LOR obj); + + void visitLREM(LREM obj); + + void visitLRETURN(LRETURN obj); + + void visitLSHL(LSHL obj); + + void visitLSHR(LSHR obj); + + void visitLSTORE(LSTORE obj); + + void visitLSUB(LSUB obj); + + void visitLUSHR(LUSHR obj); + + void visitLXOR(LXOR obj); + + void visitMONITORENTER(MONITORENTER obj); + + void visitMONITOREXIT(MONITOREXIT obj); + + void visitMULTIANEWARRAY(MULTIANEWARRAY obj); + + void visitNEW(NEW obj); + + void visitNEWARRAY(NEWARRAY obj); + + void visitNOP(NOP obj); + + void visitPOP(POP obj); + + void visitPOP2(POP2 obj); + + void visitPopInstruction(PopInstruction obj); + + void visitPushInstruction(PushInstruction obj); + + void visitPUTFIELD(PUTFIELD obj); + + void visitPUTSTATIC(PUTSTATIC obj); + + void visitRET(RET obj); + + void visitRETURN(RETURN obj); + + void visitReturnInstruction(ReturnInstruction obj); + + void visitSALOAD(SALOAD obj); + + void visitSASTORE(SASTORE obj); + + void visitSelect(Select obj); + + void visitSIPUSH(SIPUSH obj); + + void visitStackConsumer(StackConsumer obj); + + void visitStackInstruction(StackInstruction obj); + + void visitStackProducer(StackProducer obj); + + void visitStoreInstruction(StoreInstruction obj); + + void visitSWAP(SWAP obj); + + void visitTABLESWITCH(TABLESWITCH obj); + + void visitTypedInstruction(TypedInstruction obj); + + void visitUnconditionalBranch(UnconditionalBranch obj); + + void visitVariableLengthInstruction(VariableLengthInstruction obj); +} diff --git a/src/main/java/haidnor/jvm/bcel/util/AbstractClassPathRepository.java b/src/main/java/haidnor/jvm/bcel/util/AbstractClassPathRepository.java new file mode 100644 index 0000000..d96ff82 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/AbstractClassPathRepository.java @@ -0,0 +1,129 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import haidnor.jvm.bcel.classfile.ClassParser; +import haidnor.jvm.bcel.classfile.JavaClass; +import haidnor.jvm.bcel.classfile.Utility; + +import java.io.IOException; +import java.io.InputStream; + +/** + * This abstract class provides a logic of a loading {@link JavaClass} objects class names via {@link ClassPath}. + * + *

+ * Subclasses can choose caching strategy of the objects by implementing the abstract methods (e.g., + * {@link #storeClass(JavaClass)} and {@link #findClass(String)}). + *

+ * + * @since 6.4.0 + */ +abstract class AbstractClassPathRepository implements Repository { + + private final ClassPath classPath; + + AbstractClassPathRepository(final ClassPath classPath) { + this.classPath = classPath; + } + + @Override + public abstract void clear(); + + @Override + public abstract JavaClass findClass(final String className); + + @Override + public ClassPath getClassPath() { + return classPath; + } + + /** + * Finds the JavaClass object for a runtime Class object. If a class with the same name is already in this Repository, + * the Repository version is returned. Otherwise, getResourceAsStream() is called on the Class object to find the + * class's representation. If the representation is found, it is added to the Repository. + * + * @see Class + * @param clazz the runtime Class object + * @return JavaClass object for given runtime class + * @throws ClassNotFoundException if the class is not in the Repository, and its representation could not be found + */ + @Override + public JavaClass loadClass(final Class clazz) throws ClassNotFoundException { + final String className = clazz.getName(); + final JavaClass repositoryClass = findClass(className); + if (repositoryClass != null) { + return repositoryClass; + } + String name = className; + final int i = name.lastIndexOf('.'); + if (i > 0) { + name = name.substring(i + 1); + } + + try (InputStream clsStream = clazz.getResourceAsStream(name + JavaClass.EXTENSION)) { + return loadClass(clsStream, className); + } catch (final IOException e) { + return null; + } + } + + private JavaClass loadClass(final InputStream inputStream, final String className) throws ClassNotFoundException { + try { + if (inputStream != null) { + final ClassParser parser = new ClassParser(inputStream, className); + final JavaClass clazz = parser.parse(); + storeClass(clazz); + return clazz; + } + } catch (final IOException e) { + throw new ClassNotFoundException("Exception while looking for class " + className + ": " + e, e); + } + throw new ClassNotFoundException("ClassRepository could not load " + className); + } + + /** + * Finds a JavaClass object by name. If it is already in this Repository, the Repository version is returned. Otherwise, + * the Repository's classpath is searched for the class (and it is added to the Repository if found). + * + * @param className the name of the class + * @return the JavaClass object + * @throws ClassNotFoundException if the class is not in the Repository, and could not be found on the classpath + */ + @Override + public JavaClass loadClass(String className) throws ClassNotFoundException { + if (className == null || className.isEmpty()) { + throw new IllegalArgumentException("Invalid class name " + className); + } + className = Utility.pathToPackage(className); // Just in case, canonical form + final JavaClass clazz = findClass(className); + if (clazz != null) { + return clazz; + } + try (InputStream inputStream = classPath.getInputStream(className)) { + return loadClass(inputStream, className); + } catch (final IOException e) { + throw new ClassNotFoundException("Exception while looking for class " + className + ": " + e, e); + } + } + + @Override + public abstract void removeClass(final JavaClass javaClass); + + @Override + public abstract void storeClass(final JavaClass javaClass); +} diff --git a/src/main/java/haidnor/jvm/bcel/util/Args.java b/src/main/java/haidnor/jvm/bcel/util/Args.java new file mode 100644 index 0000000..523fd78 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/Args.java @@ -0,0 +1,144 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.ClassFormatException; + +/** + * Argument validation. + * + * @since 6.7.0 + */ +public class Args { + + /** + * Requires a specific value. + * + * @param value The value to test. + * @param required The required value. + * @param message The message prefix + * @return The value to test. + */ + public static int require(final int value, final int required, final String message) { + if (value != required) { + throw new ClassFormatException(String.format("%s [Value must be 0: %,d]", message, value)); + } + return value; + } + + /** + * Requires a 0 value. + * + * @param value The value to test. + * @param message The message prefix + * @return The value to test. + */ + public static int require0(final int value, final String message) { + return require(value, 0, message); + } + + /** + * Requires a u1 value. + * + * @param value The value to test. + * @param message The message prefix + * @return The value to test. + */ + public static int requireU1(final int value, final String message) { + if (value < 0 || value > Const.MAX_BYTE) { + throw new ClassFormatException(String.format("%s [Value out of range (0 - %,d) for type u1: %,d]", message, Const.MAX_BYTE, value)); + } + return value; + } + + /** + * Requires a u2 value of at least {@code min} and not above {@code max}. + * + * @param value The value to test. + * @param min The minimum required u2 value. + * @param max The maximum required u2 value. + * @param message The message prefix + * @return The value to test. + */ + public static int requireU2(final int value, final int min, final int max, final String message) { + if (max > Const.MAX_SHORT) { + throw new IllegalArgumentException(String.format("%s programming error: max %,d > %,d", message, max, Const.MAX_SHORT)); + } + if (min < 0) { + throw new IllegalArgumentException(String.format("%s programming error: min %,d < 0", message, min)); + } + if (value < min || value > max) { + throw new ClassFormatException(String.format("%s [Value out of range (%,d - %,d) for type u2: %,d]", message, min, Const.MAX_SHORT, value)); + } + return value; + } + + /** + * Requires a u2 value of at least {@code min}. + * + * @param value The value to test. + * @param min The minimum required value. + * @param message The message prefix + * @return The value to test. + */ + public static int requireU2(final int value, final int min, final String message) { + return requireU2(value, min, Const.MAX_SHORT, message); + } + + /** + * Requires a u2 value. + * + * @param value The value to test. + * @param message The message prefix + * @return The value to test. + */ + public static int requireU2(final int value, final String message) { + return requireU2(value, 0, message); + } + + /** + * Requires a u4 value of at least {@code min}. + * + * @param value The value to test. + * @param min The minimum required value. + * @param message The message prefix + * @return The value to test. + */ + public static int requireU4(final int value, final int min, final String message) { + if (min < 0) { + throw new IllegalArgumentException(String.format("%s programming error: min %,d < 0", message, min)); + } + if (value < min) { + throw new ClassFormatException( + String.format("%s [Value out of range (%,d - %,d) for type u2: %,d]", message, min, Integer.MAX_VALUE, value & 0xFFFFFFFFL)); + } + return value; + } + + /** + * Requires a u4 value. + * + * @param value The value to test. + * @param message The message prefix + * @return The value to test. + */ + public static int requireU4(final int value, final String message) { + return requireU4(value, 0, message); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/util/BCELComparator.java b/src/main/java/haidnor/jvm/bcel/util/BCELComparator.java new file mode 100644 index 0000000..818512d --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/BCELComparator.java @@ -0,0 +1,42 @@ +/* + * 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 haidnor.jvm.bcel.util; + +/** + * Used for BCEL comparison strategy + * + * @since 5.2 + */ +public interface BCELComparator { + + /** + * Compare two objects and return what THIS.equals(THAT) should return + * + * @param THIS + * @param THAT + * @return true if and only if THIS equals THAT + */ + boolean equals(Object THIS, Object THAT); + + /** + * Return hashcode for THIS.hashCode() + * + * @param THIS + * @return hashcode for THIS.hashCode() + */ + int hashCode(Object THIS); +} diff --git a/src/main/java/haidnor/jvm/bcel/util/BCELFactory.java b/src/main/java/haidnor/jvm/bcel/util/BCELFactory.java new file mode 100644 index 0000000..7a9c2c6 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/BCELFactory.java @@ -0,0 +1,302 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.classfile.Utility; +import haidnor.jvm.bcel.generic.*; + +import java.io.PrintWriter; +import java.util.*; + +/** + * Factory creates il.append() statements, and sets instruction targets. A helper class for BCELifier. + * + * @see BCELifier + */ +class BCELFactory extends EmptyVisitor { + + private static final String CONSTANT_PREFIX = Const.class.getSimpleName() + "."; + private final MethodGen methodGen; + private final PrintWriter printWriter; + private final ConstantPoolGen constantPoolGen; + + private final Map branchMap = new HashMap<>(); + + // Memorize BranchInstructions that need an update + private final List branches = new ArrayList<>(); + + BCELFactory(final MethodGen mg, final PrintWriter out) { + methodGen = mg; + constantPoolGen = mg.getConstantPool(); + printWriter = out; + } + + private void createConstant(final Object value) { + String embed = value.toString(); + if (value instanceof String) { + embed = '"' + Utility.convertString(embed) + '"'; + } else if (value instanceof Character) { + embed = "(char)0x" + Integer.toHexString(((Character) value).charValue()); + } else if (value instanceof Float) { + final Float f = (Float) value; + if (Float.isNaN(f)) { + embed = "Float.NaN"; + } else if (f == Float.POSITIVE_INFINITY) { + embed = "Float.POSITIVE_INFINITY"; + } else if (f == Float.NEGATIVE_INFINITY) { + embed = "Float.NEGATIVE_INFINITY"; + } else { + embed += "f"; + } + } else if (value instanceof Double) { + final Double d = (Double) value; + if (Double.isNaN(d)) { + embed = "Double.NaN"; + } else if (d == Double.POSITIVE_INFINITY) { + embed = "Double.POSITIVE_INFINITY"; + } else if (d == Double.NEGATIVE_INFINITY) { + embed = "Double.NEGATIVE_INFINITY"; + } else { + embed += "d"; + } + } else if (value instanceof Long) { + embed += "L"; + } else if (value instanceof ObjectType) { + final ObjectType ot = (ObjectType) value; + embed = "new ObjectType(\"" + ot.getClassName() + "\")"; + } else if (value instanceof ArrayType) { + final ArrayType at = (ArrayType) value; + embed = "new ArrayType(" + BCELifier.printType(at.getBasicType()) + ", " + at.getDimensions() + ")"; + } + + printWriter.println("il.append(new PUSH(_cp, " + embed + "));"); + } + + public void start() { + if (!methodGen.isAbstract() && !methodGen.isNative()) { + for (InstructionHandle ih = methodGen.getInstructionList().getStart(); ih != null; ih = ih.getNext()) { + final Instruction i = ih.getInstruction(); + if (i instanceof BranchInstruction) { + branchMap.put(i, ih); // memorize container + } + if (ih.hasTargeters()) { + if (i instanceof BranchInstruction) { + printWriter.println(" InstructionHandle ih_" + ih.getPosition() + ";"); + } else { + printWriter.print(" InstructionHandle ih_" + ih.getPosition() + " = "); + } + } else { + printWriter.print(" "); + } + if (!visitInstruction(i)) { + i.accept(this); + } + } + updateBranchTargets(); + updateExceptionHandlers(); + } + } + + private void updateBranchTargets() { + branches.forEach(bi -> { + final BranchHandle bh = (BranchHandle) branchMap.get(bi); + final int pos = bh.getPosition(); + final String name = bi.getName() + "_" + pos; + int targetPos = bh.getTarget().getPosition(); + printWriter.println(" " + name + ".setTarget(ih_" + targetPos + ");"); + if (bi instanceof Select) { + final InstructionHandle[] ihs = ((Select) bi).getTargets(); + for (int j = 0; j < ihs.length; j++) { + targetPos = ihs[j].getPosition(); + printWriter.println(" " + name + ".setTarget(" + j + ", ih_" + targetPos + ");"); + } + } + }); + } + + private void updateExceptionHandlers() { + final CodeExceptionGen[] handlers = methodGen.getExceptionHandlers(); + for (final CodeExceptionGen h : handlers) { + final String type = h.getCatchType() == null ? "null" : BCELifier.printType(h.getCatchType()); + printWriter.println(" method.addExceptionHandler(" + "ih_" + h.getStartPC().getPosition() + ", " + "ih_" + h.getEndPC().getPosition() + ", " + + "ih_" + h.getHandlerPC().getPosition() + ", " + type + ");"); + } + } + + @Override + public void visitAllocationInstruction(final AllocationInstruction i) { + Type type; + if (i instanceof CPInstruction) { + type = ((CPInstruction) i).getType(constantPoolGen); + } else { + type = ((NEWARRAY) i).getType(); + } + final short opcode = ((Instruction) i).getOpcode(); + int dim = 1; + switch (opcode) { + case Const.NEW: + printWriter.println("il.append(_factory.createNew(\"" + ((ObjectType) type).getClassName() + "\"));"); + break; + case Const.MULTIANEWARRAY: + dim = ((MULTIANEWARRAY) i).getDimensions(); + //$FALL-THROUGH$ + case Const.NEWARRAY: + if (type instanceof ArrayType) { + type = ((ArrayType) type).getBasicType(); + } + //$FALL-THROUGH$ + case Const.ANEWARRAY: + printWriter.println("il.append(_factory.createNewArray(" + BCELifier.printType(type) + ", (short) " + dim + "));"); + break; + default: + throw new IllegalArgumentException("Unhandled opcode: " + opcode); + } + } + + @Override + public void visitArrayInstruction(final ArrayInstruction i) { + final short opcode = i.getOpcode(); + final Type type = i.getType(constantPoolGen); + final String kind = opcode < Const.IASTORE ? "Load" : "Store"; + printWriter.println("il.append(_factory.createArray" + kind + "(" + BCELifier.printType(type) + "));"); + } + + @Override + public void visitBranchInstruction(final BranchInstruction bi) { + final BranchHandle bh = (BranchHandle) branchMap.get(bi); + final int pos = bh.getPosition(); + final String name = bi.getName() + "_" + pos; + if (bi instanceof Select) { + final Select s = (Select) bi; + branches.add(bi); + final StringBuilder args = new StringBuilder("new int[] { "); + final int[] matchs = s.getMatchs(); + for (int i = 0; i < matchs.length; i++) { + args.append(matchs[i]); + if (i < matchs.length - 1) { + args.append(", "); + } + } + args.append(" }"); + printWriter.print("Select " + name + " = new " + bi.getName().toUpperCase(Locale.ENGLISH) + "(" + args + ", new InstructionHandle[] { "); + for (int i = 0; i < matchs.length; i++) { + printWriter.print("null"); + if (i < matchs.length - 1) { + printWriter.print(", "); + } + } + printWriter.println(" }, null);"); + } else { + final int tPos = bh.getTarget().getPosition(); + String target; + if (pos > tPos) { + target = "ih_" + tPos; + } else { + branches.add(bi); + target = "null"; + } + printWriter.println(" BranchInstruction " + name + " = _factory.createBranchInstruction(" + CONSTANT_PREFIX + + bi.getName().toUpperCase(Locale.ENGLISH) + ", " + target + ");"); + } + if (bh.hasTargeters()) { + printWriter.println(" ih_" + pos + " = il.append(" + name + ");"); + } else { + printWriter.println(" il.append(" + name + ");"); + } + } + + @Override + public void visitCHECKCAST(final CHECKCAST i) { + final Type type = i.getType(constantPoolGen); + printWriter.println("il.append(_factory.createCheckCast(" + BCELifier.printType(type) + "));"); + } + + @Override + public void visitConstantPushInstruction(final ConstantPushInstruction i) { + createConstant(i.getValue()); + } + + @Override + public void visitFieldInstruction(final FieldInstruction i) { + final short opcode = i.getOpcode(); + final String className = i.getClassName(constantPoolGen); + final String fieldName = i.getFieldName(constantPoolGen); + final Type type = i.getFieldType(constantPoolGen); + printWriter.println("il.append(_factory.createFieldAccess(\"" + className + "\", \"" + fieldName + "\", " + BCELifier.printType(type) + ", " + + CONSTANT_PREFIX + Const.getOpcodeName(opcode).toUpperCase(Locale.ENGLISH) + "));"); + } + + @Override + public void visitINSTANCEOF(final INSTANCEOF i) { + final Type type = i.getType(constantPoolGen); + printWriter.println("il.append(_factory.createInstanceOf(" + BCELifier.printType(type) + "));"); + } + + private boolean visitInstruction(final Instruction i) { + final short opcode = i.getOpcode(); + if (InstructionConst.getInstruction(opcode) != null && !(i instanceof ConstantPushInstruction) && !(i instanceof ReturnInstruction)) { // Handled below + printWriter.println("il.append(InstructionConst." + i.getName().toUpperCase(Locale.ENGLISH) + ");"); + return true; + } + return false; + } + + @Override + public void visitInvokeInstruction(final InvokeInstruction i) { + final short opcode = i.getOpcode(); + final String className = i.getClassName(constantPoolGen); + final String methodName = i.getMethodName(constantPoolGen); + final Type type = i.getReturnType(constantPoolGen); + final Type[] argTypes = i.getArgumentTypes(constantPoolGen); + printWriter.println("il.append(_factory.createInvoke(\"" + className + "\", \"" + methodName + "\", " + BCELifier.printType(type) + ", " + + BCELifier.printArgumentTypes(argTypes) + ", " + CONSTANT_PREFIX + Const.getOpcodeName(opcode).toUpperCase(Locale.ENGLISH) + "));"); + } + + @Override + public void visitLDC(final LDC i) { + createConstant(i.getValue(constantPoolGen)); + } + + @Override + public void visitLDC2_W(final LDC2_W i) { + createConstant(i.getValue(constantPoolGen)); + } + + @Override + public void visitLocalVariableInstruction(final LocalVariableInstruction i) { + final short opcode = i.getOpcode(); + final Type type = i.getType(constantPoolGen); + if (opcode == Const.IINC) { + printWriter.println("il.append(new IINC(" + i.getIndex() + ", " + ((IINC) i).getIncrement() + "));"); + } else { + final String kind = opcode < Const.ISTORE ? "Load" : "Store"; + printWriter.println("il.append(_factory.create" + kind + "(" + BCELifier.printType(type) + ", " + i.getIndex() + "));"); + } + } + + @Override + public void visitRET(final RET i) { + printWriter.println("il.append(new RET(" + i.getIndex() + "));"); + } + + @Override + public void visitReturnInstruction(final ReturnInstruction i) { + final Type type = i.getType(constantPoolGen); + printWriter.println("il.append(_factory.createReturn(" + BCELifier.printType(type) + "));"); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/util/BCELifier.java b/src/main/java/haidnor/jvm/bcel/util/BCELifier.java new file mode 100644 index 0000000..6cf55b5 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/BCELifier.java @@ -0,0 +1,311 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import haidnor.jvm.bcel.Const; +import haidnor.jvm.bcel.Repository; +import haidnor.jvm.bcel.classfile.*; +import haidnor.jvm.bcel.generic.ArrayType; +import haidnor.jvm.bcel.generic.ConstantPoolGen; +import haidnor.jvm.bcel.generic.MethodGen; +import haidnor.jvm.bcel.generic.Type; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.util.Locale; + +/** + * This class takes a given JavaClass object and converts it to a Java program that creates that very class using BCEL. + * This gives new users of BCEL a useful example showing how things are done with BCEL. It does not cover all features + * of BCEL, but tries to mimic hand-written code as close as possible. + */ +public class BCELifier extends EmptyVisitor { + + /** + * Enum corresponding to flag source. + */ + public enum FLAGS { + UNKNOWN, CLASS, METHOD, + } + + // The base package name for imports; assumes Const is at the top level + // N.B we use the class so renames will be detected by the compiler/IDE + private static final String BASE_PACKAGE = Const.class.getPackage().getName(); + private static final String CONSTANT_PREFIX = Const.class.getSimpleName() + "."; + + // Needs to be accessible from unit test code + static JavaClass getJavaClass(final String name) throws ClassNotFoundException, IOException { + JavaClass javaClass; + if ((javaClass = Repository.lookupClass(name)) == null) { + javaClass = new ClassParser(name).parse(); // May throw IOException + } + return javaClass; + } + + /** + * Default main method + */ + public static void main(final String[] argv) throws Exception { + if (argv.length != 1) { + System.out.println("Usage: BCELifier className"); + System.out.println("\tThe class must exist on the classpath"); + return; + } + final BCELifier bcelifier = new BCELifier(getJavaClass(argv[0]), System.out); + bcelifier.start(); + } + + static String printArgumentTypes(final Type[] argTypes) { + if (argTypes.length == 0) { + return "Type.NO_ARGS"; + } + final StringBuilder args = new StringBuilder(); + for (int i = 0; i < argTypes.length; i++) { + args.append(printType(argTypes[i])); + if (i < argTypes.length - 1) { + args.append(", "); + } + } + return "new Type[] { " + args.toString() + " }"; + } + + static String printFlags(final int flags) { + return printFlags(flags, FLAGS.UNKNOWN); + } + + /** + * Return a string with the flag settings + * + * @param flags the flags field to interpret + * @param location the item type + * @return the formatted string + * @since 6.0 made public + */ + public static String printFlags(final int flags, final FLAGS location) { + if (flags == 0) { + return "0"; + } + final StringBuilder buf = new StringBuilder(); + for (int i = 0, pow = 1; pow <= Const.MAX_ACC_FLAG_I; i++) { + if ((flags & pow) != 0) { + if (pow == Const.ACC_SYNCHRONIZED && location == FLAGS.CLASS) { + buf.append(CONSTANT_PREFIX).append("ACC_SUPER | "); + } else if (pow == Const.ACC_VOLATILE && location == FLAGS.METHOD) { + buf.append(CONSTANT_PREFIX).append("ACC_BRIDGE | "); + } else if (pow == Const.ACC_TRANSIENT && location == FLAGS.METHOD) { + buf.append(CONSTANT_PREFIX).append("ACC_VARARGS | "); + } else if (i < Const.ACCESS_NAMES_LENGTH) { + buf.append(CONSTANT_PREFIX).append("ACC_").append(Const.getAccessName(i).toUpperCase(Locale.ENGLISH)).append(" | "); + } else { + buf.append(String.format(CONSTANT_PREFIX + "ACC_BIT %x | ", pow)); + } + } + pow <<= 1; + } + final String str = buf.toString(); + return str.substring(0, str.length() - 3); + } + + static String printType(final String signature) { + final Type type = Type.getType(signature); + final byte t = type.getType(); + if (t <= Const.T_VOID) { + return "Type." + Const.getTypeName(t).toUpperCase(Locale.ENGLISH); + } + if (type.toString().equals("java.lang.String")) { + return "Type.STRING"; + } + if (type.toString().equals("java.lang.Object")) { + return "Type.OBJECT"; + } + if (type.toString().equals("java.lang.StringBuffer")) { + return "Type.STRINGBUFFER"; + } + if (type instanceof ArrayType) { + final ArrayType at = (ArrayType) type; + return "new ArrayType(" + printType(at.getBasicType()) + ", " + at.getDimensions() + ")"; + } + return "new ObjectType(\"" + Utility.signatureToString(signature, false) + "\")"; + } + + static String printType(final Type type) { + return printType(type.getSignature()); + } + + private final JavaClass clazz; + + private final PrintWriter printWriter; + + private final ConstantPoolGen constantPoolGen; + + /** + * Constructs a new instance. + * + * @param clazz Java class to "decompile". + * @param out where to print the Java program in UTF-8. + */ + public BCELifier(final JavaClass clazz, final OutputStream out) { + this.clazz = clazz; + this.printWriter = new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8), false); + this.constantPoolGen = new ConstantPoolGen(this.clazz.getConstantPool()); + } + + private void printCreate() { + printWriter.println(" public void create(OutputStream out) throws IOException {"); + final JavaField[] fields = clazz.getFields(); + if (fields.length > 0) { + printWriter.println(" createFields();"); + } + final JavaMethod[] methods = clazz.getMethods(); + for (int i = 0; i < methods.length; i++) { + printWriter.println(" createMethod_" + i + "();"); + } + printWriter.println(" _cg.getJavaClass().dump(out);"); + printWriter.println(" }"); + printWriter.println(); + } + + private void printMain() { + final String className = clazz.getClassName(); + printWriter.println(" public static void main(String[] args) throws Exception {"); + printWriter.println(" " + className + "Creator creator = new " + className + "Creator();"); + printWriter.println(" creator.create(new FileOutputStream(\"" + className + ".class\"));"); + printWriter.println(" }"); + } + + /** + * Start Java code generation + */ + public void start() { + visitJavaClass(clazz); + printWriter.flush(); + } + + @Override + public void visitField(final JavaField field) { + printWriter.println(); + printWriter.println( + " field = new FieldGen(" + printFlags(field.getAccessFlags()) + ", " + printType(field.getSignature()) + ", \"" + field.getName() + "\", _cp);"); + final ConstantValue cv = field.getConstantValue(); + if (cv != null) { + printWriter.print(" field.setInitValue("); + if (field.getType() == Type.CHAR) { + printWriter.print("(char)"); + } + if (field.getType() == Type.SHORT) { + printWriter.print("(short)"); + } + if (field.getType() == Type.BYTE) { + printWriter.print("(byte)"); + } + printWriter.print(cv); + if (field.getType() == Type.LONG) { + printWriter.print("L"); + } + if (field.getType() == Type.FLOAT) { + printWriter.print("F"); + } + if (field.getType() == Type.DOUBLE) { + printWriter.print("D"); + } + printWriter.println(");"); + } + printWriter.println(" _cg.addField(field.getField());"); + } + + @Override + public void visitJavaClass(final JavaClass clazz) { + String className = clazz.getClassName(); + final String superName = clazz.getSuperclassName(); + final String packageName = clazz.getPackageName(); + final String inter = Utility.printArray(clazz.getInterfaceNames(), false, true); + if (StringUtils.isNotEmpty(packageName)) { + className = className.substring(packageName.length() + 1); + printWriter.println("package " + packageName + ";"); + printWriter.println(); + } + printWriter.println("import " + BASE_PACKAGE + ".generic.*;"); + printWriter.println("import " + BASE_PACKAGE + ".classfile.*;"); + printWriter.println("import " + BASE_PACKAGE + ".*;"); + printWriter.println("import java.io.*;"); + printWriter.println(); + printWriter.println("public class " + className + "Creator {"); + printWriter.println(" private InstructionFactory _factory;"); + printWriter.println(" private ConstantPoolGen _cp;"); + printWriter.println(" private ClassGen _cg;"); + printWriter.println(); + printWriter.println(" public " + className + "Creator() {"); + printWriter.println(" _cg = new ClassGen(\"" + (packageName.isEmpty() ? className : packageName + "." + className) + "\", \"" + superName + + "\", " + "\"" + clazz.getSourceFileName() + "\", " + printFlags(clazz.getAccessFlags(), FLAGS.CLASS) + ", " + "new String[] { " + inter + " });"); + printWriter.println(" _cg.setMajor(" + clazz.getMajor() + ");"); + printWriter.println(" _cg.setMinor(" + clazz.getMinor() + ");"); + printWriter.println(); + printWriter.println(" _cp = _cg.getConstantPool();"); + printWriter.println(" _factory = new InstructionFactory(_cg, _cp);"); + printWriter.println(" }"); + printWriter.println(); + printCreate(); + final JavaField[] fields = clazz.getFields(); + if (fields.length > 0) { + printWriter.println(" private void createFields() {"); + printWriter.println(" FieldGen field;"); + for (final JavaField field : fields) { + field.accept(this); + } + printWriter.println(" }"); + printWriter.println(); + } + final JavaMethod[] methods = clazz.getMethods(); + for (int i = 0; i < methods.length; i++) { + printWriter.println(" private void createMethod_" + i + "() {"); + methods[i].accept(this); + printWriter.println(" }"); + printWriter.println(); + } + printMain(); + printWriter.println("}"); + } + + @Override + public void visitMethod(final JavaMethod method) { + final MethodGen mg = new MethodGen(method, clazz.getClassName(), constantPoolGen); + printWriter.println(" InstructionList il = new InstructionList();"); + printWriter.println(" MethodGen method = new MethodGen(" + printFlags(method.getAccessFlags(), FLAGS.METHOD) + ", " + printType(mg.getReturnType()) + + ", " + printArgumentTypes(mg.getArgumentTypes()) + ", " + "new String[] { " + Utility.printArray(mg.getArgumentNames(), false, true) + " }, \"" + + method.getName() + "\", \"" + clazz.getClassName() + "\", il, _cp);"); + final ExceptionTable exceptionTable = method.getExceptionTable(); + if (exceptionTable != null) { + final String[] exceptionNames = exceptionTable.getExceptionNames(); + for (final String exceptionName : exceptionNames) { + printWriter.print(" method.addException(\""); + printWriter.print(exceptionName); + printWriter.println("\");"); + } + } + printWriter.println(); + final BCELFactory factory = new BCELFactory(mg, printWriter); + factory.start(); + printWriter.println(" method.setMaxStack();"); + printWriter.println(" method.setMaxLocals();"); + printWriter.println(" _cg.addMethod(method.getMethod());"); + printWriter.println(" il.dispose();"); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/util/ByteSequence.java b/src/main/java/haidnor/jvm/bcel/util/ByteSequence.java new file mode 100644 index 0000000..f91d394 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/ByteSequence.java @@ -0,0 +1,60 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; + +/** + * Utility class that implements a sequence of bytes which can be read via the 'readByte()' method. This is used to + * implement a wrapper for the Java byte code stream to gain some more readability. + */ +public final class ByteSequence extends DataInputStream { + + private static final class ByteArrayStream extends ByteArrayInputStream { + + ByteArrayStream(final byte[] bytes) { + super(bytes); + } + + int getPosition() { + // pos is protected in ByteArrayInputStream + return pos; + } + + void unreadByte() { + if (pos > 0) { + pos--; + } + } + } + + private final ByteArrayStream byteStream; + + public ByteSequence(final byte[] bytes) { + super(new ByteArrayStream(bytes)); + byteStream = (ByteArrayStream) in; + } + + public int getIndex() { + return byteStream.getPosition(); + } + + void unreadByte() { + byteStream.unreadByte(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/util/ClassPath.java b/src/main/java/haidnor/jvm/bcel/util/ClassPath.java new file mode 100644 index 0000000..31b1bb9 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/ClassPath.java @@ -0,0 +1,781 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import haidnor.jvm.bcel.classfile.JavaClass; +import haidnor.jvm.bcel.classfile.Utility; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Loads class files from the CLASSPATH. Inspired by sun.tools.ClassPath. + */ +public class ClassPath implements Closeable { + + private abstract static class AbstractPathEntry implements Closeable { + + abstract ClassFile getClassFile(String name, String suffix); + + abstract URL getResource(String name); + + abstract InputStream getResourceAsStream(String name); + } + + private abstract static class AbstractZip extends AbstractPathEntry { + + private final ZipFile zipFile; + + AbstractZip(final ZipFile zipFile) { + this.zipFile = Objects.requireNonNull(zipFile, "zipFile"); + } + + @Override + public void close() throws IOException { + if (zipFile != null) { + zipFile.close(); + } + + } + + @Override + ClassFile getClassFile(final String name, final String suffix) { + final ZipEntry entry = zipFile.getEntry(toEntryName(name, suffix)); + + if (entry == null) { + return null; + } + + return new ClassFile() { + + @Override + public String getBase() { + return zipFile.getName(); + } + + @Override + public InputStream getInputStream() throws IOException { + return zipFile.getInputStream(entry); + } + + @Override + public String getPath() { + return entry.toString(); + } + + @Override + public long getSize() { + return entry.getSize(); + } + + @Override + public long getTime() { + return entry.getTime(); + } + }; + } + + @Override + URL getResource(final String name) { + final ZipEntry entry = zipFile.getEntry(name); + try { + return entry != null ? new URL("jar:file:" + zipFile.getName() + "!/" + name) : null; + } catch (final MalformedURLException e) { + return null; + } + } + + @Override + InputStream getResourceAsStream(final String name) { + final ZipEntry entry = zipFile.getEntry(name); + try { + return entry != null ? zipFile.getInputStream(entry) : null; + } catch (final IOException e) { + return null; + } + } + + protected abstract String toEntryName(final String name, final String suffix); + + @Override + public String toString() { + return zipFile.getName(); + } + + } + + /** + * Contains information about file/ZIP entry of the Java class. + */ + public interface ClassFile { + + /** + * @return base path of found class, i.e. class is contained relative to that path, which may either denote a directory, + * or zip file + */ + String getBase(); + + /** + * @return input stream for class file. + * @throws IOException if an I/O error occurs. + */ + InputStream getInputStream() throws IOException; + + /** + * @return canonical path to class file. + */ + String getPath(); + + /** + * @return size of class file. + */ + long getSize(); + + /** + * @return modification time of class file. + */ + long getTime(); + } + + private static class Dir extends AbstractPathEntry { + + private final String dir; + + Dir(final String d) { + dir = d; + } + + @Override + public void close() throws IOException { + // Nothing to do + + } + + @Override + ClassFile getClassFile(final String name, final String suffix) { + final File file = new File(dir + File.separatorChar + name.replace('.', File.separatorChar) + suffix); + return file.exists() ? new ClassFile() { + + @Override + public String getBase() { + return dir; + } + + @Override + public InputStream getInputStream() throws IOException { + return new FileInputStream(file); + } + + @Override + public String getPath() { + try { + return file.getCanonicalPath(); + } catch (final IOException e) { + return null; + } + } + + @Override + public long getSize() { + return file.length(); + } + + @Override + public long getTime() { + return file.lastModified(); + } + } : null; + } + + @Override + URL getResource(final String name) { + // Resource specification uses '/' whatever the platform + final File file = toFile(name); + try { + return file.exists() ? file.toURI().toURL() : null; + } catch (final MalformedURLException e) { + return null; + } + } + + @Override + InputStream getResourceAsStream(final String name) { + // Resource specification uses '/' whatever the platform + final File file = toFile(name); + try { + return file.exists() ? new FileInputStream(file) : null; + } catch (final IOException e) { + return null; + } + } + + private File toFile(final String name) { + return new File(dir + File.separatorChar + name.replace('/', File.separatorChar)); + } + + @Override + public String toString() { + return dir; + } + } + + private static class Jar extends AbstractZip { + + Jar(final ZipFile zip) { + super(zip); + } + + @Override + protected String toEntryName(final String name, final String suffix) { + return Utility.packageToPath(name) + suffix; + } + + } + + private static class JrtModule extends AbstractPathEntry { + + private final Path modulePath; + + public JrtModule(final Path modulePath) { + this.modulePath = Objects.requireNonNull(modulePath, "modulePath"); + } + + @Override + public void close() throws IOException { + // Nothing to do. + + } + + @Override + ClassFile getClassFile(final String name, final String suffix) { + final Path resolved = modulePath.resolve(Utility.packageToPath(name) + suffix); + if (Files.exists(resolved)) { + return new ClassFile() { + + @Override + public String getBase() { + return Objects.toString(resolved.getFileName(), null); + } + + @Override + public InputStream getInputStream() throws IOException { + return Files.newInputStream(resolved); + } + + @Override + public String getPath() { + return resolved.toString(); + } + + @Override + public long getSize() { + try { + return Files.size(resolved); + } catch (final IOException e) { + return 0; + } + } + + @Override + public long getTime() { + try { + return Files.getLastModifiedTime(resolved).toMillis(); + } catch (final IOException e) { + return 0; + } + } + }; + } + return null; + } + + @Override + URL getResource(final String name) { + final Path resovled = modulePath.resolve(name); + try { + return Files.exists(resovled) ? new URL("jrt:" + modulePath + "/" + name) : null; + } catch (final MalformedURLException e) { + return null; + } + } + + @Override + InputStream getResourceAsStream(final String name) { + try { + return Files.newInputStream(modulePath.resolve(name)); + } catch (final IOException e) { + return null; + } + } + + @Override + public String toString() { + return modulePath.toString(); + } + + } + + private static class JrtModules extends AbstractPathEntry { + + private final ModularRuntimeImage modularRuntimeImage; + private final JrtModule[] modules; + + public JrtModules(final String path) throws IOException { + this.modularRuntimeImage = new ModularRuntimeImage(); + this.modules = modularRuntimeImage.list(path).stream().map(JrtModule::new).toArray(JrtModule[]::new); + } + + @Override + public void close() throws IOException { + if (modules != null) { + // don't use a for each loop to avoid creating an iterator for the GC to collect. + for (final JrtModule module : modules) { + module.close(); + } + } + if (modularRuntimeImage != null) { + modularRuntimeImage.close(); + } + } + + @Override + ClassFile getClassFile(final String name, final String suffix) { + // don't use a for each loop to avoid creating an iterator for the GC to collect. + for (final JrtModule module : modules) { + final ClassFile classFile = module.getClassFile(name, suffix); + if (classFile != null) { + return classFile; + } + } + return null; + } + + @Override + URL getResource(final String name) { + // don't use a for each loop to avoid creating an iterator for the GC to collect. + for (final JrtModule module : modules) { + final URL url = module.getResource(name); + if (url != null) { + return url; + } + } + return null; + } + + @Override + InputStream getResourceAsStream(final String name) { + // don't use a for each loop to avoid creating an iterator for the GC to collect. + for (final JrtModule module : modules) { + final InputStream inputStream = module.getResourceAsStream(name); + if (inputStream != null) { + return inputStream; + } + } + return null; + } + + @Override + public String toString() { + return Arrays.toString(modules); + } + + } + + private static class Module extends AbstractZip { + + Module(final ZipFile zip) { + super(zip); + } + + @Override + protected String toEntryName(final String name, final String suffix) { + return "classes/" + Utility.packageToPath(name) + suffix; + } + + } + + private static final FilenameFilter ARCHIVE_FILTER = (dir, name) -> { + name = name.toLowerCase(Locale.ENGLISH); + return name.endsWith(".zip") || name.endsWith(".jar"); + }; + + private static final FilenameFilter MODULES_FILTER = (dir, name) -> { + name = name.toLowerCase(Locale.ENGLISH); + return name.endsWith(haidnor.jvm.bcel.classfile.Module.EXTENSION); + }; + + public static final ClassPath SYSTEM_CLASS_PATH = new ClassPath(getClassPath()); + + private static void addJdkModules(final String javaHome, final List list) { + String modulesPath = System.getProperty("java.modules.path"); + if (modulesPath == null || modulesPath.trim().isEmpty()) { + // Default to looking in JAVA_HOME/jmods + modulesPath = javaHome + File.separator + "jmods"; + } + final File modulesDir = new File(modulesPath); + if (modulesDir.exists()) { + final String[] modules = modulesDir.list(MODULES_FILTER); + if (modules != null) { + for (final String module : modules) { + list.add(modulesDir.getPath() + File.separatorChar + module); + } + } + } + } + + /** + * Checks for class path components in the following properties: "java.class.path", "sun.boot.class.path", + * "java.ext.dirs" + * + * @return class path as used by default by BCEL + */ + // @since 6.0 no longer final + public static String getClassPath() { + final String classPathProp = System.getProperty("java.class.path"); + final String bootClassPathProp = System.getProperty("sun.boot.class.path"); + final String extDirs = System.getProperty("java.ext.dirs"); + // System.out.println("java.version = " + System.getProperty("java.version")); + // System.out.println("java.class.path = " + classPathProp); + // System.out.println("sun.boot.class.path=" + bootClassPathProp); + // System.out.println("java.ext.dirs=" + extDirs); + final String javaHome = System.getProperty("java.home"); + final List list = new ArrayList<>(); + + // Starting in JRE 9, .class files are in the modules directory. Add them to the path. + final Path modulesPath = Paths.get(javaHome).resolve("lib/modules"); + if (Files.exists(modulesPath) && Files.isRegularFile(modulesPath)) { + list.add(modulesPath.toAbsolutePath().toString()); + } + // Starting in JDK 9, .class files are in the jmods directory. Add them to the path. + addJdkModules(javaHome, list); + + getPathComponents(classPathProp, list); + getPathComponents(bootClassPathProp, list); + final List dirs = new ArrayList<>(); + getPathComponents(extDirs, dirs); + for (final String d : dirs) { + final File extDir = new File(d); + final String[] extensions = extDir.list(ARCHIVE_FILTER); + if (extensions != null) { + for (final String extension : extensions) { + list.add(extDir.getPath() + File.separatorChar + extension); + } + } + } + + return list.stream().collect(Collectors.joining(File.pathSeparator)); + } + + private static void getPathComponents(final String path, final List list) { + if (path != null) { + final StringTokenizer tokenizer = new StringTokenizer(path, File.pathSeparator); + while (tokenizer.hasMoreTokens()) { + final String name = tokenizer.nextToken(); + final File file = new File(name); + if (file.exists()) { + list.add(name); + } + } + } + } + + private final String classPathString; + + private final ClassPath parent; + + private final List paths; + + /** + * Search for classes in CLASSPATH. + * + * @deprecated Use SYSTEM_CLASS_PATH constant + */ + @Deprecated + public ClassPath() { + this(getClassPath()); + } + + @SuppressWarnings("resource") + public ClassPath(final ClassPath parent, final String classPathString) { + this.parent = parent; + this.classPathString = Objects.requireNonNull(classPathString, "classPathString"); + this.paths = new ArrayList<>(); + for (final StringTokenizer tokenizer = new StringTokenizer(classPathString, File.pathSeparator); tokenizer.hasMoreTokens();) { + final String path = tokenizer.nextToken(); + if (!path.isEmpty()) { + final File file = new File(path); + try { + if (file.exists()) { + if (file.isDirectory()) { + paths.add(new Dir(path)); + } else if (path.endsWith(haidnor.jvm.bcel.classfile.Module.EXTENSION)) { + paths.add(new Module(new ZipFile(file))); + } else if (path.endsWith(ModularRuntimeImage.MODULES_PATH)) { + paths.add(new JrtModules(ModularRuntimeImage.MODULES_PATH)); + } else { + paths.add(new Jar(new ZipFile(file))); + } + } + } catch (final IOException e) { + if (path.endsWith(".zip") || path.endsWith(".jar")) { + System.err.println("CLASSPATH component " + file + ": " + e); + } + } + } + } + } + + /** + * Search for classes in given path. + * + * @param classPath + */ + public ClassPath(final String classPath) { + this(null, classPath); + } + + @Override + public void close() throws IOException { + for (final AbstractPathEntry path : paths) { + path.close(); + } + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ClassPath other = (ClassPath) obj; + return Objects.equals(classPathString, other.classPathString); + } + + /** + * @param name fully qualified file name, e.g. java/lang/String + * @return byte array for class + * @throws IOException if an I/O error occurs. + */ + public byte[] getBytes(final String name) throws IOException { + return getBytes(name, JavaClass.EXTENSION); + } + + /** + * @param name fully qualified file name, e.g. java/lang/String + * @param suffix file name ends with suffix, e.g. .java + * @return byte array for file on class path + * @throws IOException if an I/O error occurs. + */ + public byte[] getBytes(final String name, final String suffix) throws IOException { + DataInputStream dis = null; + try (InputStream inputStream = getInputStream(name, suffix)) { + if (inputStream == null) { + throw new IOException("Couldn't find: " + name + suffix); + } + dis = new DataInputStream(inputStream); + final byte[] bytes = new byte[inputStream.available()]; + dis.readFully(bytes); + return bytes; + } finally { + if (dis != null) { + dis.close(); + } + } + } + + /** + * @param name fully qualified class name, e.g. java.lang.String + * @return input stream for class + * @throws IOException if an I/O error occurs. + */ + public ClassFile getClassFile(final String name) throws IOException { + return getClassFile(name, JavaClass.EXTENSION); + } + + /** + * @param name fully qualified file name, e.g. java/lang/String + * @param suffix file name ends with suff, e.g. .java + * @return class file for the java class + * @throws IOException if an I/O error occurs. + */ + public ClassFile getClassFile(final String name, final String suffix) throws IOException { + ClassFile cf = null; + + if (parent != null) { + cf = parent.getClassFileInternal(name, suffix); + } + + if (cf == null) { + cf = getClassFileInternal(name, suffix); + } + + if (cf != null) { + return cf; + } + + throw new IOException("Couldn't find: " + name + suffix); + } + + private ClassFile getClassFileInternal(final String name, final String suffix) { + for (final AbstractPathEntry path : paths) { + final ClassFile cf = path.getClassFile(name, suffix); + if (cf != null) { + return cf; + } + } + return null; + } + + /** + * Gets an InputStream. + *

+ * The caller is responsible for closing the InputStream. + *

+ * @param name fully qualified class name, e.g. java.lang.String + * @return input stream for class + * @throws IOException if an I/O error occurs. + */ + public InputStream getInputStream(final String name) throws IOException { + return getInputStream(Utility.packageToPath(name), JavaClass.EXTENSION); + } + + /** + * Gets an InputStream for a class or resource on the classpath. + *

+ * The caller is responsible for closing the InputStream. + *

+ * + * @param name fully qualified file name, e.g. java/lang/String + * @param suffix file name ends with suff, e.g. .java + * @return input stream for file on class path + * @throws IOException if an I/O error occurs. + */ + public InputStream getInputStream(final String name, final String suffix) throws IOException { + try { + final java.lang.ClassLoader classLoader = getClass().getClassLoader(); + @SuppressWarnings("resource") // closed by caller + final + InputStream inputStream = classLoader == null ? null : classLoader.getResourceAsStream(name + suffix); + if (inputStream != null) { + return inputStream; + } + } catch (final Exception ignored) { + // ignored + } + return getClassFile(name, suffix).getInputStream(); + } + + /** + * @param name name of file to search for, e.g. java/lang/String.java + * @return full (canonical) path for file + * @throws IOException if an I/O error occurs. + */ + public String getPath(String name) throws IOException { + final int index = name.lastIndexOf('.'); + String suffix = ""; + if (index > 0) { + suffix = name.substring(index); + name = name.substring(0, index); + } + return getPath(name, suffix); + } + + /** + * @param name name of file to search for, e.g. java/lang/String + * @param suffix file name suffix, e.g. .java + * @return full (canonical) path for file, if it exists + * @throws IOException if an I/O error occurs. + */ + public String getPath(final String name, final String suffix) throws IOException { + return getClassFile(name, suffix).getPath(); + } + + /** + * @param name fully qualified resource name, e.g. java/lang/String.class + * @return URL supplying the resource, or null if no resource with that name. + * @since 6.0 + */ + public URL getResource(final String name) { + for (final AbstractPathEntry path : paths) { + URL url; + if ((url = path.getResource(name)) != null) { + return url; + } + } + return null; + } + + /** + * @param name fully qualified resource name, e.g. java/lang/String.class + * @return InputStream supplying the resource, or null if no resource with that name. + * @since 6.0 + */ + public InputStream getResourceAsStream(final String name) { + for (final AbstractPathEntry path : paths) { + InputStream is; + if ((is = path.getResourceAsStream(name)) != null) { + return is; + } + } + return null; + } + + /** + * @param name fully qualified resource name, e.g. java/lang/String.class + * @return An Enumeration of URLs supplying the resource, or an empty Enumeration if no resource with that name. + * @since 6.0 + */ + public Enumeration getResources(final String name) { + final Vector results = new Vector<>(); + for (final AbstractPathEntry path : paths) { + URL url; + if ((url = path.getResource(name)) != null) { + results.add(url); + } + } + return results.elements(); + } + + @Override + public int hashCode() { + return classPathString.hashCode(); + } + + /** + * @return used class path string + */ + @Override + public String toString() { + if (parent != null) { + return parent + File.pathSeparator + classPathString; + } + return classPathString; + } +} diff --git a/src/main/java/haidnor/jvm/bcel/util/ClassQueue.java b/src/main/java/haidnor/jvm/bcel/util/ClassQueue.java new file mode 100644 index 0000000..8de6f32 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/ClassQueue.java @@ -0,0 +1,50 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import haidnor.jvm.bcel.classfile.JavaClass; + +import java.util.LinkedList; + +/** + * Utility class implementing a (typesafe) queue of JavaClass objects. + */ +public class ClassQueue { + + /** + * @deprecated (since 6.0) will be made private; do not access + */ + @Deprecated + protected LinkedList vec = new LinkedList<>(); // TODO not used externally + + public JavaClass dequeue() { + return vec.removeFirst(); + } + + public boolean empty() { + return vec.isEmpty(); + } + + public void enqueue(final JavaClass clazz) { + vec.addLast(clazz); + } + + @Override + public String toString() { + return vec.toString(); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/util/JavaWrapper.java b/src/main/java/haidnor/jvm/bcel/util/JavaWrapper.java new file mode 100644 index 0000000..5040160 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/JavaWrapper.java @@ -0,0 +1,115 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import org.apache.commons.lang3.StringUtils; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Java interpreter replacement, i.e., wrapper that uses its own ClassLoader to modify/generate classes as they're + * requested. You can take this as a template for your own applications. + *

+ * Call this wrapper with: + *

+ * + *
+ * java org.apache.bcel.util.JavaWrapper <real.class.name> [arguments]
+ * 
+ *

+ * To use your own class loader you can set the "bcel.classloader" system property. + *

+ * + *
+ * java org.apache.bcel.util.JavaWrapper -Dbcel.classloader=foo.MyLoader <real.class.name> [arguments]
+ * 
+ * + * @see ClassLoader + */ +public class JavaWrapper { + + private static java.lang.ClassLoader getClassLoader() { + final String s = System.getProperty("bcel.classloader"); + if (StringUtils.isEmpty(s)) { + throw new IllegalStateException("The property 'bcel.classloader' must be defined"); + } + try { + return (java.lang.ClassLoader) Class.forName(s).newInstance(); + } catch (final Exception e) { + throw new IllegalStateException(e.toString(), e); + } + } + + /** + * Default main method used as wrapper, expects the fully qualified class name of the real class as the first argument. + */ + public static void main(final String[] argv) throws Exception { + /* + * Expects class name as first argument, other arguments are by-passed. + */ + if (argv.length == 0) { + System.out.println("Missing class name."); + return; + } + final String className = argv[0]; + final String[] newArgv = new String[argv.length - 1]; + System.arraycopy(argv, 1, newArgv, 0, newArgv.length); + new JavaWrapper().runMain(className, newArgv); + } + + private final java.lang.ClassLoader loader; + + public JavaWrapper() { + this(getClassLoader()); + } + + public JavaWrapper(final java.lang.ClassLoader loader) { + this.loader = loader; + } + + /** + * Runs the main method of the given class with the arguments passed in argv + * + * @param className the fully qualified class name + * @param argv the arguments just as you would pass them directly + * @throws ClassNotFoundException if {@code className} can't be found. + */ + public void runMain(final String className, final String[] argv) throws ClassNotFoundException { + final Class cl = loader.loadClass(className); + Method method = null; + try { + method = cl.getMethod("main", argv.getClass()); + /* + * Method main is sane ? + */ + final int m = method.getModifiers(); + final Class r = method.getReturnType(); + if (!(Modifier.isPublic(m) && Modifier.isStatic(m)) || Modifier.isAbstract(m) || r != Void.TYPE) { + throw new NoSuchMethodException(); + } + } catch (final NoSuchMethodException no) { + System.out.println("In class " + className + ": public static void main(String[] argv) is not defined"); + return; + } + try { + method.invoke(null, (Object[]) argv); + } catch (final Exception ex) { + ex.printStackTrace(); + } + } +} diff --git a/src/main/java/haidnor/jvm/bcel/util/MemorySensitiveClassPathRepository.java b/src/main/java/haidnor/jvm/bcel/util/MemorySensitiveClassPathRepository.java new file mode 100644 index 0000000..e5f0f19 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/MemorySensitiveClassPathRepository.java @@ -0,0 +1,76 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import haidnor.jvm.bcel.Repository; +import haidnor.jvm.bcel.classfile.JavaClass; + +import java.lang.ref.SoftReference; +import java.util.HashMap; +import java.util.Map; + +/** + * This repository is used in situations where a Class is created outside the realm of a ClassLoader. Classes are loaded + * from the file systems using the paths specified in the given class path. By default, this is the value returned by + * ClassPath.getClassPath(). This repository holds onto classes with SoftReferences, and will reload as needed, in cases + * where memory sizes are important. + * + * @see Repository + */ +public class MemorySensitiveClassPathRepository extends AbstractClassPathRepository { + + private final Map> loadedClasses = new HashMap<>(); // CLASSNAME X JAVACLASS + + public MemorySensitiveClassPathRepository(final ClassPath path) { + super(path); + } + + /** + * Clear all entries from cache. + */ + @Override + public void clear() { + loadedClasses.clear(); + } + + /** + * Find an already defined (cached) JavaClass object by name. + */ + @Override + public JavaClass findClass(final String className) { + final SoftReference ref = loadedClasses.get(className); + return ref == null ? null : ref.get(); + } + + /** + * Remove class from repository + */ + @Override + public void removeClass(final JavaClass clazz) { + loadedClasses.remove(clazz.getClassName()); + } + + /** + * Store a new JavaClass instance into this Repository. + */ + @Override + public void storeClass(final JavaClass clazz) { + // Not calling super.storeClass because this subclass maintains the mapping. + loadedClasses.put(clazz.getClassName(), new SoftReference<>(clazz)); + clazz.setRepository(this); + } +} diff --git a/src/main/java/haidnor/jvm/bcel/util/ModularRuntimeImage.java b/src/main/java/haidnor/jvm/bcel/util/ModularRuntimeImage.java new file mode 100644 index 0000000..38dc24e --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/ModularRuntimeImage.java @@ -0,0 +1,132 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Wraps a Java 9 JEP 220 modular runtime image. Requires the JRT NIO file system. + * + * @since 6.3 + */ +public class ModularRuntimeImage implements Closeable { + + static final String MODULES_PATH = File.separator + "modules"; + static final String PACKAGES_PATH = File.separator + "packages"; + + private final URLClassLoader classLoader; + private final FileSystem fileSystem; + + /** + * Constructs a default instance. + */ + @SuppressWarnings("resource") // See #close() + public ModularRuntimeImage() { + this(null, FileSystems.getFileSystem(URI.create("jrt:/"))); + } + + /** + * Constructs an instance using the JRT file system implementation from a specific Java Home. + * + * @param javaHome Path to a Java 9 or greater home. + * + * @throws IOException an I/O error occurs accessing the file system + */ + public ModularRuntimeImage(final String javaHome) throws IOException { + final Map emptyMap = Collections.emptyMap(); + final Path jrePath = Paths.get(javaHome); + final Path jrtFsPath = jrePath.resolve("lib").resolve("jrt-fs.jar"); + this.classLoader = URLClassLoader.newInstance(new URL[] {jrtFsPath.toUri().toURL()}); + this.fileSystem = FileSystems.newFileSystem(URI.create("jrt:/"), emptyMap, classLoader); + } + + private ModularRuntimeImage(final URLClassLoader cl, final FileSystem fs) { + this.classLoader = cl; + this.fileSystem = fs; + } + + @Override + public void close() throws IOException { + if (classLoader != null) { + classLoader.close(); + } + if (fileSystem != null) { + fileSystem.close(); + } + } + + public FileSystem getFileSystem() { + return fileSystem; + } + + /** + * Lists all entries in the given directory. + * + * @param dirPath directory path. + * @return a list of dir entries if an I/O error occurs + * @throws IOException an I/O error occurs accessing the file system + */ + public List list(final Path dirPath) throws IOException { + final List list = new ArrayList<>(); + try (DirectoryStream ds = Files.newDirectoryStream(dirPath)) { + ds.forEach(list::add); + } + return list; + } + + /** + * Lists all entries in the given directory. + * + * @param dirName directory path. + * @return a list of dir entries if an I/O error occurs + * @throws IOException an I/O error occurs accessing the file system + */ + public List list(final String dirName) throws IOException { + return list(fileSystem.getPath(dirName)); + } + + /** + * Lists all modules. + * + * @return a list of modules + * @throws IOException an I/O error occurs accessing the file system + */ + public List modules() throws IOException { + return list(MODULES_PATH); + } + + /** + * Lists all packages. + * + * @return a list of modules + * @throws IOException an I/O error occurs accessing the file system + */ + public List packages() throws IOException { + return list(PACKAGES_PATH); + } + +} diff --git a/src/main/java/haidnor/jvm/bcel/util/Repository.java b/src/main/java/haidnor/jvm/bcel/util/Repository.java new file mode 100644 index 0000000..3c640e0 --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/Repository.java @@ -0,0 +1,67 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import haidnor.jvm.bcel.classfile.JavaClass; + +/** + * Abstract definition of a class repository. Instances may be used to load classes from different sources and may be + * used in the Repository.setRepository method. + * + * @see haidnor.jvm.bcel.Repository + */ +public interface Repository { + + /** + * Clears all entries from cache. + */ + void clear(); + + /** + * Finds the class with the name provided, if the class isn't there, return NULL. + */ + JavaClass findClass(String className); + + /** + * Gets the ClassPath associated with this Repository + */ + ClassPath getClassPath(); + + /** + * Finds the JavaClass instance for the given run-time class object. + * + * @throws ClassNotFoundException if the class can't be found. + */ + JavaClass loadClass(Class clazz) throws ClassNotFoundException; + + /** + * Finds the class with the name provided, if the class isn't there, make an attempt to load it. + * + * @throws ClassNotFoundException if the class can't be found. + */ + JavaClass loadClass(String className) throws ClassNotFoundException; + + /** + * Removes class from repository + */ + void removeClass(JavaClass clazz); + + /** + * Stores the provided class under "clazz.getClassName()" + */ + void storeClass(JavaClass clazz); +} diff --git a/src/main/java/haidnor/jvm/bcel/util/SyntheticRepository.java b/src/main/java/haidnor/jvm/bcel/util/SyntheticRepository.java new file mode 100644 index 0000000..a54dfba --- /dev/null +++ b/src/main/java/haidnor/jvm/bcel/util/SyntheticRepository.java @@ -0,0 +1,50 @@ +/* + * 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 haidnor.jvm.bcel.util; + +import haidnor.jvm.bcel.Repository; + +import java.util.HashMap; +import java.util.Map; + +/** + * This repository is used in situations where a Class is created outside the realm of a ClassLoader. Classes are loaded + * from the file systems using the paths specified in the given class path. By default, this is the value returned by + * ClassPath.getClassPath(). + *

+ * This repository uses a factory design, allowing it to maintain a collection of different classpaths, and as such It + * is designed to be used as a singleton per classpath. + *

+ * + * @see Repository + */ +public class SyntheticRepository extends MemorySensitiveClassPathRepository { + + private static final Map MAP = new HashMap<>(); // CLASSPATH X REPOSITORY + + public static SyntheticRepository getInstance() { + return getInstance(ClassPath.SYSTEM_CLASS_PATH); + } + + public static SyntheticRepository getInstance(final ClassPath classPath) { + return MAP.computeIfAbsent(classPath, SyntheticRepository::new); + } + + private SyntheticRepository(final ClassPath path) { + super(path); + } +} diff --git a/src/main/java/haidnor/jvm/classloader/ClassLoader.java b/src/main/java/haidnor/jvm/classloader/ClassLoader.java deleted file mode 100644 index 8c4cb79..0000000 --- a/src/main/java/haidnor/jvm/classloader/ClassLoader.java +++ /dev/null @@ -1,86 +0,0 @@ -package haidnor.jvm.classloader; - -import haidnor.jvm.rtda.Klass; -import org.apache.bcel.classfile.ClassParser; -import org.apache.bcel.classfile.JavaClass; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -/** - * @author wang xiang - */ -public class ClassLoader { - - public final String name; - - public final static String JAVA_HOME; - - /** - * java.base.jmod 文件路径 - */ - public final static String javaBaseJmodPath; - - public JarFile jarFile = null; - - static { - JAVA_HOME = System.getenv( "JAVA_HOME"); - javaBaseJmodPath = JAVA_HOME + "/jmods/java.base.jmod"; - } - - public ClassLoader(String name) { - this.name = name; - } - - public ClassLoader(JarFile jarFile, String name) { - this.name = name; - this.jarFile = jarFile; - } - - /** - * @param classPath 类路径,例如 haidnor/jvm/classloader/ClassLoader - */ - public Klass loadClass(String classPath) throws IOException { - ClassParser classParser = null; - if (classPath.startsWith("java/")) { - classParser = new ClassParser(javaBaseJmodPath, "classes/" + classPath + ".class"); - } else if (jarFile != null) { - Enumeration entries = jarFile.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - if (!entry.isDirectory() && entry.getName().endsWith(".class")) { - String className = entry.getName().substring(0, entry.getName().length() - 6); - if (className.equals(classPath)) { - InputStream inputStream = jarFile.getInputStream(entry); - classParser = new ClassParser(inputStream, null); - } - } - } - } else { - URL resource = this.getClass().getResource("/"); - String fileName = resource.getPath() + classPath + ".class"; - classParser = new ClassParser(fileName); - } - - JavaClass javaClass = classParser.parse(); - return new Klass(this, javaClass); - } - - public Klass loadClass(JarFile jarFile, JarEntry entry) throws IOException { - InputStream inputStream = jarFile.getInputStream(entry); - ClassParser classParser = new ClassParser(inputStream, null); - JavaClass javaClass = classParser.parse(); - return new Klass(this, javaClass); - } - - public Klass loadClassWithAbsolutePath(String absolutePath) throws IOException { - ClassParser classParser = new ClassParser(absolutePath); - JavaClass javaClass = classParser.parse(); - return new Klass(this, javaClass); - } - -} \ No newline at end of file diff --git a/src/main/java/haidnor/jvm/core/JavaExecutionEngine.java b/src/main/java/haidnor/jvm/core/JavaExecutionEngine.java deleted file mode 100644 index 16eaf9f..0000000 --- a/src/main/java/haidnor/jvm/core/JavaExecutionEngine.java +++ /dev/null @@ -1,173 +0,0 @@ -package haidnor.jvm.core; - - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.instruction.InstructionFactory; -import haidnor.jvm.instruction.control.*; -import haidnor.jvm.rtda.KlassMethod; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.JVMThread; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.JvmThreadHolder; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.*; - -import java.util.HashMap; -import java.util.Map; - -/** - * JVM 字节码执行引擎 - * - * @author wang xiang - */ -@Slf4j -public class JavaExecutionEngine { - - /** - * 执行 public static void main(String[] args) 方法 - */ - public static void callMainMethod(KlassMethod klassMethod) { - callMethod(null, klassMethod); - } - - /** - * 执行普通方法 - * - * @param lastFrame 方法调用者的栈帧 - * @param klassMethod 方法元信息 - */ - public static void callMethod(Frame lastFrame, KlassMethod klassMethod) { - JVMThread jvmThread = JvmThreadHolder.get(); - // 调用方法时会创建新的栈帧 - Frame newFrame = new Frame(jvmThread, klassMethod); - - // 如果线程栈内存在栈帧, 代表可能需要方法调用传参 - if (lastFrame != null) { - Method method = klassMethod.javaMethod; - String signature = method.getSignature(); - String[] argumentTypes = Utility.methodSignatureArgumentTypes(signature); - - int argumentSlotSize = argumentTypes.length; - if (!method.isStatic()) { - argumentSlotSize++; - } - - // 方法调用传参 (原理: 将顶部栈帧的操作数栈中的数据弹出, 存入新栈帧的局部变量表中) - LocalVariableTable localVariableTable = method.getLocalVariableTable(); - if (localVariableTable != null) { - for (int i = argumentSlotSize - 1; i >= 0; i--) { - LocalVariable[] localVariableArr = localVariableTable.getLocalVariableTable(); - LocalVariable localVariable = localVariableArr[i]; - int slotIndex = localVariable.getIndex(); - StackValue stackValue = lastFrame.pop(); - newFrame.slotSet(slotIndex, stackValue); - } - } - } - - // 将新栈帧压入线程栈顶部, 并执行新栈帧中的代码 - jvmThread.push(newFrame); - executeFrame(newFrame); - } - - /** - * 执行 JVM 线程栈栈帧 (haidnorJVM 中最关键的代码) - * - * @author wang xiang - */ - @SneakyThrows - public static void executeFrame(Frame frame) { - int stackSize = frame.getJvmThread().stackSize(); - - String blank = getDebugStackInfoBlank(stackSize); - log.debug("{}┌──────────────────[{}] {} | {}", blank, stackSize, frame.klass.getClassName(), frame.getMethod()); - - // 解析方法中的字节码指令 - Map instructionMap = new HashMap<>(); - CodeStream codeStream = frame.getCodeStream(); - while (codeStream.available() > 0) { - Instruction instruction = InstructionFactory.creatInstruction(codeStream); - instructionMap.put(instruction.index(), instruction); - // log.debug("{}│> {}", blank, instruction); // debug 输出解析字节码指令的内容 - } - // log.debug("{}├ - - - - - - - - -", blank); - - // 执行方法中的字节码指令 - // 提示: 变量 pc 相当于程序计数器, 记录当前执行到的字节码指令的"行号" - for (int pc = 0; pc < frame.getCodeLength(); ) { - Instruction instruction = instructionMap.get(pc); - log.debug("{}│ {}", blank, instruction); - try { - // 执行字节码指令 - instruction.execute(frame); - // 若为 return 系列指令则结束当前栈帧 (RETURN,ARETURN,DRETURN,FRETURN,IRETURN) - if (instruction instanceof ReturnableInstruction) { - break; - } - // 程序计数器值增加. 指向下一次执行的字节码行号 - pc += instruction.offSet(); - } - // 捕获执行字节码指令抛出的异常 - catch (Exception exception) { - // 从类元信息中获取异常表 - CodeException[] exceptionTable = frame.getMethod().getCode().getExceptionTable(); - - // handlerPC 是从异常表中获取的处理异常的字节码行号 (如果查询不到就会一直为 null. 代表方法无异常处理代码) - Integer handlerPC = null; - for (CodeException codeException : exceptionTable) { - if (codeException.getStartPC() <= pc && pc <= codeException.getEndPC()) { - // 异常类型 (0 代表捕获所有类型的异常) - int catchType = codeException.getCatchType(); - if (catchType == 0) { - frame.push(new StackValue(Const.T_OBJECT, exception)); - handlerPC = codeException.getHandlerPC(); - } else { - // 从常量池中查询异常表定义的异常类型 - String exceptionClassName = frame.getConstantPoolUtil().constantClass_ClassName(catchType); - exceptionClassName = Utility.compactClassName(exceptionClassName, false); - - // 判断异常的泛型类型. 假如执行指令抛出的是 NullPointerException 类型, 异常表定义的是 Exception 类型, 则此异常可以被捕获 - Class exceptionClass = Class.forName(exceptionClassName); - if (exceptionClass.isAssignableFrom(exception.getClass())) { - frame.push(new StackValue(Const.T_OBJECT, exception)); - handlerPC = codeException.getHandlerPC(); - } - } - } - } - if (handlerPC != null) { - // 将程序计数器跳转到处理异常的字节码指令上 - pc = handlerPC; - } else { - // 方法无异常处理,结束栈帧. 将异常抛给调用者处理 - log.debug("{}└──────────────────[{}] No Exception Handler Return!", blank, stackSize); - throw exception; - } - } - - } - - log.debug("{}└──────────────────[{}] {} | {}", blank, stackSize, frame.klass.getClassName(), frame.getMethod()); - } - - /** - * 计算绘画 jvm 线程栈 debug 日志信息的空格 - * 用于表现栈的调用关系 - * - * @param stackSize 当前 JVM 线程栈的栈帧数量 - */ - private static String getDebugStackInfoBlank(int stackSize) { - StringBuilder blank = new StringBuilder(); - blank.append(" ".repeat(stackSize - 1)); - int index = 0; - for (int i = 0; i < stackSize - 1; i++) { - blank.replace(index, index + 1, "│"); - index += 20; - } - return blank.toString(); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/Instruction.java b/src/main/java/haidnor/jvm/instruction/Instruction.java deleted file mode 100644 index 7007ffd..0000000 --- a/src/main/java/haidnor/jvm/instruction/Instruction.java +++ /dev/null @@ -1,48 +0,0 @@ -package haidnor.jvm.instruction; - -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -/** - * @author wang xiang - */ -public abstract class Instruction { - /** - * 指令在字节码 code 数组中的索引下标 (可以理解为指令的行号) - */ - private final int index; - - /** - * 执行下一个指令的偏移量 - */ - private int offSet = 1; - - public Instruction(CodeStream codeStream) { - this.index = codeStream.index(); - } - - /** - * 执行执行 - * - * @param frame 当前 JVM 线程栈的栈帧 - */ - public abstract void execute(Frame frame); - - public int index() { - return index; - } - - public final int offSet() { - return this.offSet; - } - - public void setOffSet(int offSet) { - this.offSet = offSet; - } - - @Override - public String toString() { - return index + " " + this.getClass().getSimpleName(); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/InstructionFactory.java b/src/main/java/haidnor/jvm/instruction/InstructionFactory.java deleted file mode 100644 index b71c56d..0000000 --- a/src/main/java/haidnor/jvm/instruction/InstructionFactory.java +++ /dev/null @@ -1,631 +0,0 @@ -package haidnor.jvm.instruction; - -import haidnor.jvm.instruction.comparisons.*; -import haidnor.jvm.instruction.constants.*; -import haidnor.jvm.instruction.control.*; -import haidnor.jvm.instruction.conversions.*; -import haidnor.jvm.instruction.extended.*; -import haidnor.jvm.instruction.loads.*; -import haidnor.jvm.instruction.math.*; -import haidnor.jvm.instruction.references.*; -import haidnor.jvm.instruction.stack.*; -import haidnor.jvm.instruction.stores.*; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * @author wang xiang - */ -public abstract class InstructionFactory { - - public static Instruction creatInstruction(CodeStream codeStream) { - int opcode = codeStream.readJavaVmOpcode(); - switch (opcode) { - case Const.NOP -> { - return new NOP(codeStream); - } - case Const.ACONST_NULL -> { - return new ACONST_NULL(codeStream); - } - case Const.ICONST_M1 -> { - return new ICONST_M1(codeStream); - } - case Const.ICONST_0 -> { - return new ICONST_0(codeStream); - } - case Const.ICONST_1 -> { - return new ICONST_1(codeStream); - } - case Const.ICONST_2 -> { - return new ICONST_2(codeStream); - } - case Const.ICONST_3 -> { - return new ICONST_3(codeStream); - } - case Const.ICONST_4 -> { - return new ICONST_4(codeStream); - } - case Const.ICONST_5 -> { - return new ICONST_5(codeStream); - } - case Const.LCONST_0 -> { - return new LCONST_0(codeStream); - } - case Const.LCONST_1 -> { - return new LCONST_1(codeStream); - } - case Const.FCONST_1 -> { - return new FCONST_1(codeStream); - } - case Const.FCONST_2 -> { - return new FCONST_2(codeStream); - } - case Const.DCONST_0 -> { - return new DCONST_0(codeStream); - } - case Const.DCONST_1 -> { - return new DCONST_1(codeStream); - } - case Const.BIPUSH -> { - return new BIPUSH(codeStream); - } - case Const.SIPUSH -> { - return new SIPUSH(codeStream); - } - case Const.LDC -> { - return new LDC(codeStream); - } - case Const.LDC_W -> { - return new LDC_W(codeStream); - } - case Const.LDC2_W -> { - return new LDC2W(codeStream); - } - case Const.ILOAD -> { - return new ILOAD(codeStream); - } - case Const.LLOAD -> { - return new LLOAD(codeStream); - } - case Const.FLOAD -> { - return new FLOAD(codeStream); - } - case Const.DLOAD -> { - return new DLOAD(codeStream); - } - case Const.ALOAD -> { - return new ALOAD(codeStream); - } - case Const.ILOAD_0 -> { - return new ILOAD_0(codeStream); - } - case Const.ILOAD_1 -> { - return new ILOAD_1(codeStream); - } - case Const.ILOAD_2 -> { - return new ILOAD_2(codeStream); - } - case Const.ILOAD_3 -> { - return new ILOAD_3(codeStream); - } - case Const.LLOAD_0 -> { - return new LLOAD_0(codeStream); - } - case Const.LLOAD_1 -> { - return new LLOAD_1(codeStream); - } - case Const.LLOAD_2 -> { - return new LLOAD_2(codeStream); - } - case Const.LLOAD_3 -> { - return new LLOAD_3(codeStream); - } - case Const.FLOAD_0 -> { - return new FLOAD_0(codeStream); - } - case Const.FLOAD_1 -> { - return new FLOAD_1(codeStream); - } - case Const.FLOAD_2 -> { - return new FLOAD_2(codeStream); - } - case Const.FLOAD_3 -> { - return new FLOAD_3(codeStream); - } - case Const.DLOAD_0 -> { - return new DLOAD_0(codeStream); - } - case Const.DLOAD_1 -> { - return new DLOAD_1(codeStream); - } - case Const.DLOAD_2 -> { - return new DLOAD_2(codeStream); - } - case Const.DLOAD_3 -> { - return new DLOAD_3(codeStream); - } - case Const.ALOAD_0 -> { - return new ALOAD_0(codeStream); - } - case Const.ALOAD_1 -> { - return new ALOAD_1(codeStream); - } - case Const.ALOAD_2 -> { - return new ALOAD_2(codeStream); - } - case Const.ALOAD_3 -> { - return new ALOAD_3(codeStream); - } - case Const.IALOAD -> { - return new IALOAD(codeStream); - } - case Const.LALOAD -> { - return new LALOAD(codeStream); - } - case Const.FALOAD -> { - return new FALOAD(codeStream); - } - case Const.DALOAD -> { - return new DALOAD(codeStream); - } - case Const.AALOAD -> { - return new AALOAD(codeStream); - } - case Const.BALOAD -> { - return new BALOAD(codeStream); - } - case Const.CALOAD -> { - return new CALOAD(codeStream); - } - case Const.SALOAD -> { - return new SALOAD(codeStream); - } - case Const.ISTORE -> { - return new ISTORE(codeStream); - } - case Const.LSTORE -> { - return new LSTORE(codeStream); - } - case Const.FSTORE -> { - return new FSTORE(codeStream); - } - case Const.DSTORE -> { - return new DSTORE(codeStream); - } - case Const.ASTORE -> { - return new ASTORE(codeStream); - } - case Const.ISTORE_0 -> { - return new ISTORE_0(codeStream); - } - case Const.ISTORE_1 -> { - return new ISTORE_1(codeStream); - } - case Const.ISTORE_2 -> { - return new ISTORE_2(codeStream); - } - case Const.ISTORE_3 -> { - return new ISTORE_3(codeStream); - } - case Const.LSTORE_0 -> { - return new LSTORE_0(codeStream); - } - case Const.LSTORE_1 -> { - return new LSTORE_1(codeStream); - } - case Const.LSTORE_2 -> { - return new LSTORE_2(codeStream); - } - case Const.LSTORE_3 -> { - return new LSTORE_3(codeStream); - } - case Const.FSTORE_0 -> { - return new FSTORE_0(codeStream); - } - case Const.FSTORE_1 -> { - return new FSTORE_1(codeStream); - } - case Const.FSTORE_2 -> { - return new FSTORE_2(codeStream); - } - case Const.FSTORE_3 -> { - return new FSTORE_3(codeStream); - } - case Const.DSTORE_0 -> { - return new DSTORE_0(codeStream); - } - case Const.DSTORE_1 -> { - return new DSTORE_1(codeStream); - } - case Const.DSTORE_2 -> { - return new DSTORE_2(codeStream); - } - case Const.DSTORE_3 -> { - return new DSTORE_3(codeStream); - } - case Const.ASTORE_0 -> { - return new ASTORE_0(codeStream); - } - case Const.ASTORE_1 -> { - return new ASTORE_1(codeStream); - } - case Const.ASTORE_2 -> { - return new ASTORE_2(codeStream); - } - case Const.ASTORE_3 -> { - return new ASTORE_3(codeStream); - } - case Const.IASTORE -> { - return new IASTORE(codeStream); - } - case Const.LASTORE -> { - return new LASTORE(codeStream); - } - case Const.FASTORE -> { - return new FASTORE(codeStream); - } - case Const.DASTORE -> { - return new DASTORE(codeStream); - } - case Const.AASTORE -> { - return new AASTORE(codeStream); - } - case Const.BASTORE -> { - return new BASTORE(codeStream); - } - case Const.CASTORE -> { - return new CASTORE(codeStream); - } - case Const.SASTORE -> { - return new SASTORE(codeStream); - } - case Const.POP -> { - return new POP(codeStream); - } - case Const.POP2 -> { - return new POP2(codeStream); - } - case Const.DUP -> { - return new DUP(codeStream); - } - case Const.DUP_X1 -> { - return new DUP_X1(codeStream); - } - case Const.DUP_X2 -> { - return new DUP_X2(codeStream); - } - case Const.DUP2 -> { - return new DUP2(codeStream); - } - case Const.DUP2_X1 -> { - return new DUP2_X1(codeStream); - } - case Const.DUP2_X2 -> { - return new DUP2_X2(codeStream); - } - case Const.SWAP -> { - return new SWAP(codeStream); - } - case Const.IADD -> { - return new IADD(codeStream); - } - case Const.LADD -> { - return new LADD(codeStream); - } - case Const.FADD -> { - return new FADD(codeStream); - } - case Const.DADD -> { - return new DADD(codeStream); - } - case Const.ISUB -> { - return new ISUB(codeStream); - } - case Const.LSUB -> { - return new LSUB(codeStream); - } - case Const.FSUB -> { - return new FSUB(codeStream); - } - case Const.DSUB -> { - return new DSUB(codeStream); - } - case Const.IMUL -> { - return new IMUL(codeStream); - } - case Const.LMUL -> { - return new LMUL(codeStream); - } - case Const.FMUL -> { - return new FMUL(codeStream); - } - case Const.DMUL -> { - return new DMUL(codeStream); - } - case Const.IDIV -> { - return new IDIV(codeStream); - } - case Const.LDIV -> { - return new LDIV(codeStream); - } - case Const.FDIV -> { - return new FDIV(codeStream); - } - case Const.DDIV -> { - return new DDIV(codeStream); - } - case Const.IREM -> { - return new IREM(codeStream); - } - case Const.LREM -> { - return new LREM(codeStream); - } - case Const.FREM -> { - return new FREM(codeStream); - } - case Const.DREM -> { - return new DREM(codeStream); - } - case Const.INEG -> { - return new INEG(codeStream); - } - case Const.LNEG -> { - return new LNEG(codeStream); - } - case Const.FNEG -> { - return new FNEG(codeStream); - } - case Const.DNEG -> { - return new DNEG(codeStream); - } - case Const.ISHL -> { - return new ISHL(codeStream); - } - case Const.LSHL -> { - return new LSHL(codeStream); - } - case Const.ISHR -> { - return new ISHR(codeStream); - } - case Const.LSHR -> { - return new LSHR(codeStream); - } - case Const.IUSHR -> { - return new IUSHR(codeStream); - } - case Const.LUSHR -> { - return new LUSHR(codeStream); - } - case Const.IAND -> { - return new IAND(codeStream); - } - case Const.LAND -> { - return new LAND(codeStream); - } - case Const.IOR -> { - return new IOR(codeStream); - } - case Const.LOR -> { - return new LOR(codeStream); - } - case Const.IXOR -> { - return new IXOR(codeStream); - } - case Const.LXOR -> { - return new LXOR(codeStream); - } - case Const.IINC -> { - return new IINC(codeStream); - } - case Const.I2L -> { - return new I2L(codeStream); - } - case Const.I2F -> { - return new I2F(codeStream); - } - case Const.I2D -> { - return new I2D(codeStream); - } - case Const.L2I -> { - return new L2I(codeStream); - } - case Const.L2F -> { - return new L2F(codeStream); - } - case Const.L2D -> { - return new L2D(codeStream); - } - case Const.F2I -> { - return new F2I(codeStream); - } - case Const.F2L -> { - return new F2L(codeStream); - } - case Const.F2D -> { - return new F2D(codeStream); - } - case Const.D2I -> { - return new D2I(codeStream); - } - case Const.D2L -> { - return new D2L(codeStream); - } - case Const.D2F -> { - return new D2F(codeStream); - } - case Const.I2B -> { - return new I2B(codeStream); - } - case Const.I2C -> { - return new I2C(codeStream); - } - case Const.I2S -> { - return new I2S(codeStream); - } - case Const.LCMP -> { - return new LCMP(codeStream); - } - case Const.FCMPL -> { - return new FCMPL(codeStream); - } - case Const.FCMPG -> { - return new FCMPG(codeStream); - } - case Const.DCMPL -> { - return new DCMPL(codeStream); - } - case Const.DCMPG -> { - return new DCMPG(codeStream); - } - case Const.IFEQ -> { - return new IFEQ(codeStream); - } - case Const.IFNE -> { - return new IFNE(codeStream); - } - case Const.IFLT -> { - return new IFLT(codeStream); - } - case Const.IFGE -> { - return new IFGE(codeStream); - } - case Const.IFGT -> { - return new IFGT(codeStream); - } - case Const.IFLE -> { - return new IFLE(codeStream); - } - case Const.IF_ICMPEQ -> { - return new IF_ICMPEQ(codeStream); - } - case Const.IF_ICMPNE -> { - return new IF_ICMPNE(codeStream); - } - case Const.IF_ICMPLT -> { - return new IF_ICMPLT(codeStream); - } - case Const.IF_ICMPGE -> { - return new IF_ICMPGE(codeStream); - } - case Const.IF_ICMPGT -> { - return new IF_ICMPGT(codeStream); - } - case Const.IF_ICMPLE -> { - return new IF_ICMPLE(codeStream); - } - case Const.IF_ACMPEQ -> { - return new IF_ACMPEQ(codeStream); - } - case Const.IF_ACMPNE -> { - return new IF_ACMPNE(codeStream); - } - case Const.GOTO -> { - return new GOTO(codeStream); - } - case Const.JSR -> { - return new JSR(codeStream); - } - case Const.RET -> { - return new RET(codeStream); - } - case Const.TABLESWITCH -> { - return new TABLESWITCH(codeStream); // TODO - } - case Const.LOOKUPSWITCH -> { - return new LOOKUPSWITCH(codeStream); // TODO - } - case Const.IRETURN -> { - return new IRETURN(codeStream); - } - case Const.LRETURN -> { - return new LRETURN(codeStream); - } - case Const.FRETURN -> { - return new FRETURN(codeStream); - } - case Const.DRETURN -> { - return new DRETURN(codeStream); - } - case Const.ARETURN -> { - return new ARETURN(codeStream); - } - case Const.RETURN -> { - return new RETURN(codeStream); - } - case Const.GETSTATIC -> { - return new GETSTATIC(codeStream); - } - case Const.PUTSTATIC -> { - return new PUTSTATIC(codeStream); - } - case Const.GETFIELD -> { - return new GETFIELD(codeStream); - } - case Const.PUTFIELD -> { - return new PUTFIELD(codeStream); - } - case Const.INVOKEVIRTUAL -> { - return new INVOKEVIRTUAL(codeStream); - } - case Const.INVOKESPECIAL -> { - return new INVOKESPECIAL(codeStream); - } - case Const.INVOKESTATIC -> { - return new INVOKESTATIC(codeStream); - } - case Const.INVOKEINTERFACE -> { - return new INVOKEINTERFACE(codeStream); - } - case Const.INVOKEDYNAMIC -> { - return new INVOKEDYNAMIC(codeStream); // TODO - } - case Const.NEW -> { - return new NEW(codeStream); - } - case Const.NEWARRAY -> { - return new NEWARRAY(codeStream); - } - case Const.ANEWARRAY -> { - return new ANEWARRAY(codeStream); - } - case Const.ARRAYLENGTH -> { - return new ARRAYLENGTH(codeStream); - } - case Const.ATHROW -> { - return new ATHROW(codeStream); - } - case Const.CHECKCAST -> { - return new CHECKCAST(codeStream); - } - case Const.INSTANCEOF -> { - return new INSTANCEOF(codeStream); - } - case Const.MONITORENTER -> { - return new MONITORENTER(codeStream); - } - case Const.MONITOREXIT -> { - return new MONITOREXIT(codeStream); - } - case Const.WIDE -> { - return new WIDE(codeStream); - } - case Const.MULTIANEWARRAY -> { - return new MULTIANEWARRAY(codeStream); - } - case Const.IFNULL -> { - return new IFNULL(codeStream); - } - case Const.IFNONNULL -> { - return new IFNONNULL(codeStream); - } - case Const.GOTO_W -> { - return new GOTO_W(codeStream); - } - case Const.JSR_W -> { - return new JSR_W(codeStream); - } - default -> throw new Error("Unknown JavaVM opcode " + opcode); - } - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/DCMPG.java b/src/main/java/haidnor/jvm/instruction/comparisons/DCMPG.java deleted file mode 100644 index 3fe4f61..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/DCMPG.java +++ /dev/null @@ -1,31 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -/** - * @author wang xiang - */ -public class DCMPG extends Instruction { - - public DCMPG(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - double v2 = frame.popDouble(); - double v1 = frame.popDouble(); - if (v1 == v2) { - frame.pushInt(0); - return; - } - if (v1 < v2) { - frame.pushInt(-1); - return; - } - frame.pushInt(1); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/DCMPL.java b/src/main/java/haidnor/jvm/instruction/comparisons/DCMPL.java deleted file mode 100644 index 0f2b8f3..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/DCMPL.java +++ /dev/null @@ -1,31 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -/** - * @author wang xiang - */ -public class DCMPL extends Instruction { - - public DCMPL(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - Double v2 = frame.popDouble(); - Double v1 = frame.popDouble(); - if (v1.equals(v2)) { - frame.pushInt(0); - return; - } - if (v1 < v2) { - frame.pushInt(-1); - return; - } - frame.pushInt(1); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/FCMPG.java b/src/main/java/haidnor/jvm/instruction/comparisons/FCMPG.java deleted file mode 100644 index d57fd7e..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/FCMPG.java +++ /dev/null @@ -1,30 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -/** - * @author wang xiang - */ -public class FCMPG extends Instruction { - - public FCMPG(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - float v2 = frame.popFloat(); - float v1 = frame.popFloat(); - if (v1 == v2) { - frame.pushInt(0); - return; - } - if (v1 < v2) { - frame.pushInt(-1); - return; - } - frame.pushInt(1); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/FCMPL.java b/src/main/java/haidnor/jvm/instruction/comparisons/FCMPL.java deleted file mode 100644 index 5dba400..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/FCMPL.java +++ /dev/null @@ -1,30 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -/** - * @author wang xiang - */ -public class FCMPL extends Instruction { - - public FCMPL(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - float v2 = frame.popFloat(); - float v1 = frame.popFloat(); - if (v1 == v2) { - frame.pushInt(0); - return; - } - if (v1 < v2) { - frame.pushInt(-1); - return; - } - frame.pushInt(1); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IFEQ.java b/src/main/java/haidnor/jvm/instruction/comparisons/IFEQ.java deleted file mode 100644 index 3b8e3be..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IFEQ.java +++ /dev/null @@ -1,40 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class IFEQ extends Instruction { - - private final int offSet; - - public IFEQ(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v1 = frame.pop(); - if (v1.getValue() instanceof Boolean) { - if (!((boolean) v1.getValue())) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } else { - if ((int) v1.getValue() == 0) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IFGE.java b/src/main/java/haidnor/jvm/instruction/comparisons/IFGE.java deleted file mode 100644 index 1ff335f..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IFGE.java +++ /dev/null @@ -1,34 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class IFGE extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IFGE(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v1 = frame.pop(); - if ((int) v1.getValue() >= 0) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IFGT.java b/src/main/java/haidnor/jvm/instruction/comparisons/IFGT.java deleted file mode 100644 index 5a18f0b..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IFGT.java +++ /dev/null @@ -1,34 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class IFGT extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IFGT(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v1 = frame.pop(); - if ((int) v1.getValue() > 0) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IFLE.java b/src/main/java/haidnor/jvm/instruction/comparisons/IFLE.java deleted file mode 100644 index 819f5cc..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IFLE.java +++ /dev/null @@ -1,34 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class IFLE extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IFLE(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v1 = frame.pop(); - if ((int) v1.getValue() <= 0) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IFLT.java b/src/main/java/haidnor/jvm/instruction/comparisons/IFLT.java deleted file mode 100644 index 45bbba2..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IFLT.java +++ /dev/null @@ -1,34 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class IFLT extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IFLT(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v1 = frame.pop(); - if ((int) v1.getValue() < 0) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IFNE.java b/src/main/java/haidnor/jvm/instruction/comparisons/IFNE.java deleted file mode 100644 index 292b569..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IFNE.java +++ /dev/null @@ -1,43 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class IFNE extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IFNE(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v1 = frame.pop(); - if (v1.getValue() instanceof Boolean) { - if (((boolean) v1.getValue())) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } else { - if ((int) v1.getValue() != 0) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ACMPEQ.java b/src/main/java/haidnor/jvm/instruction/comparisons/IF_ACMPEQ.java deleted file mode 100644 index 2d9fb6f..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ACMPEQ.java +++ /dev/null @@ -1,36 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -/** - * @author wang xiang - */ -public class IF_ACMPEQ extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IF_ACMPEQ(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - Object val2 = frame.popRef(); - Object val1 = frame.popRef(); - if (val1 == val2) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ACMPNE.java b/src/main/java/haidnor/jvm/instruction/comparisons/IF_ACMPNE.java deleted file mode 100644 index 0579494..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ACMPNE.java +++ /dev/null @@ -1,36 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -/** - * @author wang xiang - */ -public class IF_ACMPNE extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IF_ACMPNE(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - Object val2 = frame.popRef(); - Object val1 = frame.popRef(); - if (val1 != val2) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPEQ.java b/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPEQ.java deleted file mode 100644 index dd64736..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPEQ.java +++ /dev/null @@ -1,42 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -/** - * Java VM opcode. - * - * @author wang xiang - * @see Opcode - * definitions in The Java Virtual Machine Specification - */ -public class IF_ICMPEQ extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IF_ICMPEQ(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v1 = frame.pop(); - StackValue v2 = frame.pop(); - - if ((int) v1.getValue() == (int) v2.getValue()) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPGE.java b/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPGE.java deleted file mode 100644 index d7cca6d..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPGE.java +++ /dev/null @@ -1,42 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -/** - * Java VM opcode. - * - * @see Opcode - * definitions in The Java Virtual Machine Specification - */ -public class IF_ICMPGE extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IF_ICMPGE(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v2 = frame.pop(); - StackValue v1 = frame.pop(); - - if ((int) v1.getValue() >= (int) v2.getValue()) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPGT.java b/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPGT.java deleted file mode 100644 index dae8e92..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPGT.java +++ /dev/null @@ -1,42 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -/** - * Java VM opcode. - * - * @see Opcode - * definitions in The Java Virtual Machine Specification - */ -public class IF_ICMPGT extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IF_ICMPGT(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v2 = frame.pop(); - StackValue v1 = frame.pop(); - - if ((int) v1.getValue() > (int) v2.getValue()) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPLE.java b/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPLE.java deleted file mode 100644 index 73d7768..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPLE.java +++ /dev/null @@ -1,37 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -/** - * Java VM opcode. - * - * @see Opcode - * definitions in The Java Virtual Machine Specification - */ -public class IF_ICMPLE extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IF_ICMPLE(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v2 = frame.pop(); - StackValue v1 = frame.pop(); - - if ((int) v1.getValue() <= (int) v2.getValue()) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPLT.java b/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPLT.java deleted file mode 100644 index d60cb76..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPLT.java +++ /dev/null @@ -1,42 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -/** - * Java VM opcode. - * - * @see Opcode - * definitions in The Java Virtual Machine Specification - */ -public class IF_ICMPLT extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IF_ICMPLT(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v2 = frame.pop(); - StackValue v1 = frame.pop(); - - if ((int) v1.getValue() < (int) v2.getValue()) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPNE.java b/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPNE.java deleted file mode 100644 index cc5696f..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/IF_ICMPNE.java +++ /dev/null @@ -1,42 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -/** - * Java VM opcode. - * - * @see Opcode - * definitions in The Java Virtual Machine Specification - */ -public class IF_ICMPNE extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IF_ICMPNE(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v1 = frame.pop(); - StackValue v2 = frame.pop(); - - if ((int) v1.getValue() != (int) v2.getValue()) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/comparisons/LCMP.java b/src/main/java/haidnor/jvm/instruction/comparisons/LCMP.java deleted file mode 100644 index 60e3daf..0000000 --- a/src/main/java/haidnor/jvm/instruction/comparisons/LCMP.java +++ /dev/null @@ -1,28 +0,0 @@ -package haidnor.jvm.instruction.comparisons; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class LCMP extends Instruction { - - public LCMP(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - Long v2 = frame.popLong(); - Long v1 = frame.popLong(); - if (v1.equals(v2)) { - frame.pushInt(0); - return; - } - if (v1 < v2) { - frame.pushInt(-1); - return; - } - frame.pushInt(1); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/ACONST_NULL.java b/src/main/java/haidnor/jvm/instruction/constants/ACONST_NULL.java deleted file mode 100644 index 13d834a..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/ACONST_NULL.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class ACONST_NULL extends Instruction { - - public ACONST_NULL(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_OBJECT, null)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/BIPUSH.java b/src/main/java/haidnor/jvm/instruction/constants/BIPUSH.java deleted file mode 100644 index 8fb95be..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/BIPUSH.java +++ /dev/null @@ -1,29 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * Java VM opcode. - * - * @see Opcode definitions in - * The Java Virtual Machine Specification - */ -public class BIPUSH extends Instruction { - - private final int value; - - public BIPUSH(CodeStream codeStream) { - super(codeStream); - this.value = codeStream.readByte(this); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_INT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/DCONST_0.java b/src/main/java/haidnor/jvm/instruction/constants/DCONST_0.java deleted file mode 100644 index b22fe45..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/DCONST_0.java +++ /dev/null @@ -1,26 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * Java VM opcode. - * - * @see Opcode definitions - * in The Java Virtual Machine Specification - */ -public class DCONST_0 extends Instruction { - - public DCONST_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_DOUBLE, 0.0D)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/DCONST_1.java b/src/main/java/haidnor/jvm/instruction/constants/DCONST_1.java deleted file mode 100644 index 6123104..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/DCONST_1.java +++ /dev/null @@ -1,26 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * Java VM opcode. - * - * @see Opcode definitions - * in The Java Virtual Machine Specification - */ -public class DCONST_1 extends Instruction { - - public DCONST_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_DOUBLE, 1.0D)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/FCONST_1.java b/src/main/java/haidnor/jvm/instruction/constants/FCONST_1.java deleted file mode 100644 index 4f22abe..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/FCONST_1.java +++ /dev/null @@ -1,26 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * Java VM opcode. - * - * @see Opcode definitions - * in The Java Virtual Machine Specification - */ -public class FCONST_1 extends Instruction { - - public FCONST_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_FLOAT, 1.0f)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/FCONST_2.java b/src/main/java/haidnor/jvm/instruction/constants/FCONST_2.java deleted file mode 100644 index 2ddeda6..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/FCONST_2.java +++ /dev/null @@ -1,26 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * Java VM opcode. - * - * @see Opcode definitions - * in The Java Virtual Machine Specification - */ -public class FCONST_2 extends Instruction { - - public FCONST_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_FLOAT, 2.0f)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/ICONST_0.java b/src/main/java/haidnor/jvm/instruction/constants/ICONST_0.java deleted file mode 100644 index 7ee6c0b..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/ICONST_0.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-6.html#jvms-6.5.iconst_i - */ -public class ICONST_0 extends Instruction { - - public ICONST_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_INT, 0)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/ICONST_1.java b/src/main/java/haidnor/jvm/instruction/constants/ICONST_1.java deleted file mode 100644 index daf2b0f..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/ICONST_1.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-6.html#jvms-6.5.iconst_i - */ -public class ICONST_1 extends Instruction { - - public ICONST_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_INT, 1)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/ICONST_2.java b/src/main/java/haidnor/jvm/instruction/constants/ICONST_2.java deleted file mode 100644 index 5478225..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/ICONST_2.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-6.html#jvms-6.5.iconst_i - */ -public class ICONST_2 extends Instruction { - - public ICONST_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_INT, 2)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/ICONST_3.java b/src/main/java/haidnor/jvm/instruction/constants/ICONST_3.java deleted file mode 100644 index e17f8c4..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/ICONST_3.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-6.html#jvms-6.5.iconst_i - */ -public class ICONST_3 extends Instruction { - - public ICONST_3(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_INT, 3)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/ICONST_4.java b/src/main/java/haidnor/jvm/instruction/constants/ICONST_4.java deleted file mode 100644 index ef73844..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/ICONST_4.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-6.html#jvms-6.5.iconst_i - */ -public class ICONST_4 extends Instruction { - - public ICONST_4(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_INT, 4)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/ICONST_5.java b/src/main/java/haidnor/jvm/instruction/constants/ICONST_5.java deleted file mode 100644 index c45a4bd..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/ICONST_5.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-6.html#jvms-6.5.iconst_i - */ -public class ICONST_5 extends Instruction { - - public ICONST_5(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_INT, 5)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/ICONST_M1.java b/src/main/java/haidnor/jvm/instruction/constants/ICONST_M1.java deleted file mode 100644 index 7913a7f..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/ICONST_M1.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-6.html#jvms-6.5.iconst_i - */ -public class ICONST_M1 extends Instruction { - - public ICONST_M1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_INT, -1)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/LCONST_0.java b/src/main/java/haidnor/jvm/instruction/constants/LCONST_0.java deleted file mode 100644 index f2ba981..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/LCONST_0.java +++ /dev/null @@ -1,26 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * Java VM opcode. - * - * @see Opcode definitions - * in The Java Virtual Machine Specification - */ -public class LCONST_0 extends Instruction { - - public LCONST_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_LONG, 0L)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/LCONST_1.java b/src/main/java/haidnor/jvm/instruction/constants/LCONST_1.java deleted file mode 100644 index 694fa21..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/LCONST_1.java +++ /dev/null @@ -1,26 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * Java VM opcode. - * - * @see Opcode definitions - * in The Java Virtual Machine Specification - */ -public class LCONST_1 extends Instruction { - - public LCONST_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_LONG, 1L)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/LDC.java b/src/main/java/haidnor/jvm/instruction/constants/LDC.java deleted file mode 100644 index 822d44c..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/LDC.java +++ /dev/null @@ -1,58 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.*; - -/** - * Java VM opcode. - * - * @see Opcode definitions in The - * Java Virtual Machine Specification - */ -public class LDC extends Instruction { - - private final int constantIndex; - - public LDC(CodeStream codeStream) { - super(codeStream); - this.constantIndex = codeStream.readUnsignedByte(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ConstantPool constantPool = frame.getConstantPool(); - - // 从常量池中获取值 - Constant constant = constantPool.getConstant(constantIndex); - - switch (constant.getTag()) { - case Const.CONSTANT_Integer: { - ConstantInteger constantInteger = (ConstantInteger) constant; - Object value = constantInteger.getConstantValue(constantPool); - frame.push(new StackValue(Const.T_INT, value)); - break; - } - case Const.CONSTANT_Float: { - ConstantFloat constantFloat = (ConstantFloat) constant; - Object value = constantFloat.getConstantValue(constantPool); - frame.push(new StackValue(Const.T_FLOAT, value)); - break; - } - case Const.CONSTANT_String: { - ConstantString constString = (ConstantString) constant; - Object value = constString.getConstantValue(constantPool); - frame.push(new StackValue(Const.T_OBJECT, value)); - break; - } - default: - throw new Error("not supported LDC type" + constant.getTag()); - } - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/LDC2W.java b/src/main/java/haidnor/jvm/instruction/constants/LDC2W.java deleted file mode 100644 index ecca253..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/LDC2W.java +++ /dev/null @@ -1,48 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.Constant; -import org.apache.bcel.classfile.ConstantDouble; -import org.apache.bcel.classfile.ConstantLong; -import org.apache.bcel.classfile.ConstantPool; - -/** - * 将 long 或 double 型常量从常量池中推送至栈顶 (宽索引) - */ -public class LDC2W extends Instruction { - - private final byte constantTag; - - private long longValue; - - private double doubleValue; - - public LDC2W(CodeStream codeStream) { - super(codeStream); - int index = codeStream.readUnsignedShort(this); - ConstantPool constantPool = codeStream.getCode().getConstantPool(); - Constant constant = constantPool.getConstant(index); - this.constantTag = constant.getTag(); - if (constantTag == Const.CONSTANT_Long) { - ConstantLong constantLong = (ConstantLong) constant; - this.longValue = constantLong.getBytes(); - } else if (constantTag == Const.CONSTANT_Double) { - ConstantDouble constantDouble = (ConstantDouble) constant; - this.doubleValue = constantDouble.getBytes(); - } - } - - @Override - public void execute(Frame frame) { - if (constantTag == Const.CONSTANT_Long) { - frame.push(new StackValue(Const.T_LONG, this.longValue)); - } else if (constantTag == Const.CONSTANT_Double) { - frame.push(new StackValue(Const.T_DOUBLE, this.doubleValue)); - } - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/LDC_W.java b/src/main/java/haidnor/jvm/instruction/constants/LDC_W.java deleted file mode 100644 index a10aeac..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/LDC_W.java +++ /dev/null @@ -1,58 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.*; - -/** - * Java VM opcode. - * - * @see Opcode definitions in - * The Java Virtual Machine Specification - */ -public class LDC_W extends Instruction { - - private final int constantIndex; - - public LDC_W(CodeStream codeStream) { - super(codeStream); - this.constantIndex = codeStream.readUnsignedShort(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ConstantPool constantPool = frame.getConstantPool(); - - // 从常量池中获取值 - Constant constant = constantPool.getConstant(constantIndex); - - switch (constant.getTag()) { - case Const.CONSTANT_Integer: { - ConstantInteger constantInteger = (ConstantInteger) constant; - Object value = constantInteger.getConstantValue(constantPool); - frame.push(new StackValue(Const.T_INT, value)); - break; - } - case Const.CONSTANT_Float: { - ConstantFloat constantFloat = (ConstantFloat) constant; - Object value = constantFloat.getConstantValue(constantPool); - frame.push(new StackValue(Const.T_FLOAT, value)); - break; - } - case Const.CONSTANT_String: { - ConstantString constString = (ConstantString) constant; - Object value = constString.getConstantValue(constantPool); - frame.push(new StackValue(Const.T_OBJECT, value)); - break; - } - default: - throw new Error("not supported LDCW type" + constant.getTag()); - } - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/NOP.java b/src/main/java/haidnor/jvm/instruction/constants/NOP.java deleted file mode 100644 index 2ffdbea..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/NOP.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -/** - * Java VM opcode. - * - * @see Opcode definitions in The - * Java Virtual Machine Specification - */ -public class NOP extends Instruction { - - public NOP(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/constants/SIPUSH.java b/src/main/java/haidnor/jvm/instruction/constants/SIPUSH.java deleted file mode 100644 index 7bcc6b3..0000000 --- a/src/main/java/haidnor/jvm/instruction/constants/SIPUSH.java +++ /dev/null @@ -1,29 +0,0 @@ -package haidnor.jvm.instruction.constants; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * Java VM opcode. - * - * @see Opcode definitions in - * The Java Virtual Machine Specification - */ -public class SIPUSH extends Instruction { - - private final int value; - - public SIPUSH(CodeStream codeStream) { - super(codeStream); - this.value = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - frame.push(new StackValue(Const.T_INT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/control/ARETURN.java b/src/main/java/haidnor/jvm/instruction/control/ARETURN.java deleted file mode 100644 index 1dc6f41..0000000 --- a/src/main/java/haidnor/jvm/instruction/control/ARETURN.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.instruction.control; - -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.JvmThreadHolder; - -public class ARETURN extends ReturnableInstruction { - - public ARETURN(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - // 弹出操作数栈中的值 - StackValue stackValue = frame.pop(); - // 将当前栈帧从 jvm 线程栈中弹出 - JvmThreadHolder.get().pop(); - // 将方法返回值压入前一个栈帧的操作数栈中 - Frame topFrame = JvmThreadHolder.get().peek(); - topFrame.push(stackValue); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/control/DRETURN.java b/src/main/java/haidnor/jvm/instruction/control/DRETURN.java deleted file mode 100644 index eaab65e..0000000 --- a/src/main/java/haidnor/jvm/instruction/control/DRETURN.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.instruction.control; - -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.JvmThreadHolder; - -public class DRETURN extends ReturnableInstruction { - - public DRETURN(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - // 弹出操作数栈中的值 - StackValue stackValue = frame.pop(); - // 将当前栈帧从 jvm 线程栈中弹出 - JvmThreadHolder.get().pop(); - // 将方法返回值压入前一个栈帧的操作数栈中 - Frame topFrame = JvmThreadHolder.get().peek(); - topFrame.push(stackValue); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/control/FRETURN.java b/src/main/java/haidnor/jvm/instruction/control/FRETURN.java deleted file mode 100644 index 694df1c..0000000 --- a/src/main/java/haidnor/jvm/instruction/control/FRETURN.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.instruction.control; - -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.JvmThreadHolder; - -public class FRETURN extends ReturnableInstruction { - - public FRETURN(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - // 弹出操作数栈中的值 - StackValue stackValue = frame.pop(); - // 将当前栈帧从 jvm 线程栈中弹出 - JvmThreadHolder.get().pop(); - // 将方法返回值压入前一个栈帧的操作数栈中 - Frame topFrame = JvmThreadHolder.get().peek(); - topFrame.push(stackValue); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/control/IRETURN.java b/src/main/java/haidnor/jvm/instruction/control/IRETURN.java deleted file mode 100644 index 294ba2e..0000000 --- a/src/main/java/haidnor/jvm/instruction/control/IRETURN.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.instruction.control; - -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.JvmThreadHolder; - -public class IRETURN extends ReturnableInstruction { - - public IRETURN(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - // 弹出操作数栈中的值 - StackValue stackValue = frame.pop(); - // 将当前栈帧从 jvm 线程栈中弹出 - JvmThreadHolder.get().pop(); - // 将方法返回值压入前一个栈帧的操作数栈中 - Frame topFrame = JvmThreadHolder.get().peek(); - topFrame.push(stackValue); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/control/LOOKUPSWITCH.java b/src/main/java/haidnor/jvm/instruction/control/LOOKUPSWITCH.java deleted file mode 100644 index 87b7d6a..0000000 --- a/src/main/java/haidnor/jvm/instruction/control/LOOKUPSWITCH.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.control; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class LOOKUPSWITCH extends Instruction { - - public LOOKUPSWITCH(CodeStream codeStream) { - super(codeStream); - throw new UnsupportedOperationException("LOOKUPSWITCH"); - } - - @Override - public void execute(Frame frame) { - throw new UnsupportedOperationException("LOOKUPSWITCH"); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/control/LRETURN.java b/src/main/java/haidnor/jvm/instruction/control/LRETURN.java deleted file mode 100644 index 50de213..0000000 --- a/src/main/java/haidnor/jvm/instruction/control/LRETURN.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.instruction.control; - -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.JvmThreadHolder; - -public class LRETURN extends ReturnableInstruction { - - public LRETURN(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - // 弹出操作数栈中的值 - StackValue stackValue = frame.pop(); - // 将当前栈帧从 jvm 线程栈中弹出 - JvmThreadHolder.get().pop(); - // 将方法返回值压入前一个栈帧的操作数栈中 - Frame topFrame = JvmThreadHolder.get().peek(); - topFrame.push(stackValue); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/control/RET.java b/src/main/java/haidnor/jvm/instruction/control/RET.java deleted file mode 100644 index 18410e8..0000000 --- a/src/main/java/haidnor/jvm/instruction/control/RET.java +++ /dev/null @@ -1,28 +0,0 @@ -package haidnor.jvm.instruction.control; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -/** - * RET(Return)指令是 Java 虚拟机中的一条指令,用于从方法中返回并将控制流转移到调用该方法的位置。 - *

- * RET 指令在早期版本的 Java 虚拟机中使用,但自从 Java SE 6 版本以后,它已经被废弃不再使用。取而代之的是通过 JSR 和 RET 指令的组合实现的子例程(subroutines)已经被新的字节码指令 invokedynamic 所取代。 - *

- * RET 指令需要一个操作数,作为局部变量表(local variable table)中一个给定索引处的值。这个索引通常是由 JSR(Jump SubRoutine)指令记录的,JSR 指令在携带一个偏移量的情况下,会将指令执行的位置压入操作数栈,并跳转到指定的位置。RET 指令则使用这个记录的位置来返回。 - *

- * 需要注意的是,由于 RET 指令被废弃,所以在现代的 Java 虚拟机中,解释器或即时编译器会将 RET 指令替换为其他的指令序列来实现相同的功能。 - */ -public class RET extends Instruction { - - public RET(CodeStream codeStream) { - super(codeStream); - throw new UnsupportedOperationException("RET"); - } - - @Override - public void execute(Frame frame) { - throw new UnsupportedOperationException("RET"); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/control/RETURN.java b/src/main/java/haidnor/jvm/instruction/control/RETURN.java deleted file mode 100644 index 596082f..0000000 --- a/src/main/java/haidnor/jvm/instruction/control/RETURN.java +++ /dev/null @@ -1,18 +0,0 @@ -package haidnor.jvm.instruction.control; - -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.JvmThreadHolder; - -public class RETURN extends ReturnableInstruction { - - public RETURN(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - JvmThreadHolder.get().pop(); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/control/ReturnableInstruction.java b/src/main/java/haidnor/jvm/instruction/control/ReturnableInstruction.java deleted file mode 100644 index 5e00204..0000000 --- a/src/main/java/haidnor/jvm/instruction/control/ReturnableInstruction.java +++ /dev/null @@ -1,15 +0,0 @@ -package haidnor.jvm.instruction.control; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.util.CodeStream; - -/** - * return 系列字节码指令的抽象类 - */ -public abstract class ReturnableInstruction extends Instruction { - - public ReturnableInstruction(CodeStream codeStream) { - super(codeStream); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/control/TABLESWITCH.java b/src/main/java/haidnor/jvm/instruction/control/TABLESWITCH.java deleted file mode 100644 index d4b71ef..0000000 --- a/src/main/java/haidnor/jvm/instruction/control/TABLESWITCH.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.control; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class TABLESWITCH extends Instruction { - - public TABLESWITCH(CodeStream codeStream) { - super(codeStream); - throw new UnsupportedOperationException("TABLESWITCH"); - } - - @Override - public void execute(Frame frame) { - throw new UnsupportedOperationException("TABLESWITCH"); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/D2F.java b/src/main/java/haidnor/jvm/instruction/conversions/D2F.java deleted file mode 100644 index bbfc874..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/D2F.java +++ /dev/null @@ -1,20 +0,0 @@ - -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class D2F extends Instruction { - - public D2F(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - double doubleVal = frame.popDouble(); - frame.pushFloat(Double.valueOf(doubleVal).floatValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/D2I.java b/src/main/java/haidnor/jvm/instruction/conversions/D2I.java deleted file mode 100644 index 94f5e44..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/D2I.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class D2I extends Instruction { - - public D2I(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - double doubleVal = frame.popDouble(); - frame.pushInt(Double.valueOf(doubleVal).intValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/D2L.java b/src/main/java/haidnor/jvm/instruction/conversions/D2L.java deleted file mode 100644 index 1bba3f0..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/D2L.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class D2L extends Instruction { - - public D2L(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - double doubleVal = frame.popDouble(); - frame.pushLong(Double.valueOf(doubleVal).longValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/F2D.java b/src/main/java/haidnor/jvm/instruction/conversions/F2D.java deleted file mode 100644 index a4a0d77..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/F2D.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class F2D extends Instruction { - - public F2D(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - float floatVal = frame.popFloat(); - frame.pushDouble(Float.valueOf(floatVal).doubleValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/F2I.java b/src/main/java/haidnor/jvm/instruction/conversions/F2I.java deleted file mode 100644 index 0a95939..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/F2I.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class F2I extends Instruction { - - public F2I(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - float floatVal = frame.popFloat(); - frame.pushInt(Float.valueOf(floatVal).intValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/F2L.java b/src/main/java/haidnor/jvm/instruction/conversions/F2L.java deleted file mode 100644 index cdadf34..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/F2L.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class F2L extends Instruction { - - public F2L(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - float floatVal = frame.popFloat(); - frame.pushLong(Float.valueOf(floatVal).longValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/I2B.java b/src/main/java/haidnor/jvm/instruction/conversions/I2B.java deleted file mode 100644 index 1b84a80..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/I2B.java +++ /dev/null @@ -1,24 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -/** - * JVM中的I2B指令是用于执行将整数类型(int)转换为字节类型(byte)的指令。该指令将一个int类型的数值从操作数栈中弹出,并将其转换为一个字节(byte)。 - * 然后,将转换后的字节值压入操作数栈顶。 - *

- * byte 类型的数据是以 int 形式存在的,因此不需要做任何处理 - */ -public class I2B extends Instruction { - - public I2B(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/I2C.java b/src/main/java/haidnor/jvm/instruction/conversions/I2C.java deleted file mode 100644 index 04a9b08..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/I2C.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -/** - * char 类型的数据是以 int 形式存在的,因此不需要做任何处理 - */ -public class I2C extends Instruction { - - public I2C(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/I2D.java b/src/main/java/haidnor/jvm/instruction/conversions/I2D.java deleted file mode 100644 index e62a3dd..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/I2D.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class I2D extends Instruction { - - public I2D(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int intVal = frame.popInt(); - frame.pushDouble(Integer.valueOf(intVal).doubleValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/I2F.java b/src/main/java/haidnor/jvm/instruction/conversions/I2F.java deleted file mode 100644 index 7c4daf5..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/I2F.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class I2F extends Instruction { - - public I2F(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int intVal = frame.popInt(); - frame.pushFloat(Integer.valueOf(intVal).floatValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/I2L.java b/src/main/java/haidnor/jvm/instruction/conversions/I2L.java deleted file mode 100644 index 6bb9ac8..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/I2L.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class I2L extends Instruction { - - public I2L(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int intVal = frame.popInt(); - frame.pushLong(Integer.valueOf(intVal).longValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/I2S.java b/src/main/java/haidnor/jvm/instruction/conversions/I2S.java deleted file mode 100644 index fe9cd1e..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/I2S.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class I2S extends Instruction { - - public I2S(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int intVal = frame.popInt(); - short shortValue = Integer.valueOf(intVal).shortValue(); - frame.pushInt(((int) shortValue)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/L2D.java b/src/main/java/haidnor/jvm/instruction/conversions/L2D.java deleted file mode 100644 index fd38919..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/L2D.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class L2D extends Instruction { - - public L2D(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - long longVal = frame.popLong(); - frame.pushDouble(Long.valueOf(longVal).doubleValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/L2F.java b/src/main/java/haidnor/jvm/instruction/conversions/L2F.java deleted file mode 100644 index ae4d084..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/L2F.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class L2F extends Instruction { - - public L2F(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - long longVal = frame.popLong(); - frame.pushFloat(Long.valueOf(longVal).floatValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/conversions/L2I.java b/src/main/java/haidnor/jvm/instruction/conversions/L2I.java deleted file mode 100644 index 7e43556..0000000 --- a/src/main/java/haidnor/jvm/instruction/conversions/L2I.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.instruction.conversions; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class L2I extends Instruction { - - public L2I(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - long longVal = frame.popLong(); - frame.pushInt(Long.valueOf(longVal).intValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/extended/GOTO.java b/src/main/java/haidnor/jvm/instruction/extended/GOTO.java deleted file mode 100644 index a7fc5d4..0000000 --- a/src/main/java/haidnor/jvm/instruction/extended/GOTO.java +++ /dev/null @@ -1,33 +0,0 @@ -package haidnor.jvm.instruction.extended; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -/** - * Java VM opcode. - * - * @see Opcode definitions in - * The Java Virtual Machine Specification - */ -public class GOTO extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public GOTO(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - super.setOffSet(offSet); - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } -} diff --git a/src/main/java/haidnor/jvm/instruction/extended/GOTO_W.java b/src/main/java/haidnor/jvm/instruction/extended/GOTO_W.java deleted file mode 100644 index e79bb2b..0000000 --- a/src/main/java/haidnor/jvm/instruction/extended/GOTO_W.java +++ /dev/null @@ -1,27 +0,0 @@ -package haidnor.jvm.instruction.extended; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class GOTO_W extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public GOTO_W(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readInt(this); - } - - @Override - public void execute(Frame frame) { - super.setOffSet(offSet); - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } -} diff --git a/src/main/java/haidnor/jvm/instruction/extended/IFNONNULL.java b/src/main/java/haidnor/jvm/instruction/extended/IFNONNULL.java deleted file mode 100644 index 2c7f73c..0000000 --- a/src/main/java/haidnor/jvm/instruction/extended/IFNONNULL.java +++ /dev/null @@ -1,33 +0,0 @@ -package haidnor.jvm.instruction.extended; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class IFNONNULL extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IFNONNULL(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v1 = frame.pop(); - if (v1.getValue() != null) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } -} diff --git a/src/main/java/haidnor/jvm/instruction/extended/IFNULL.java b/src/main/java/haidnor/jvm/instruction/extended/IFNULL.java deleted file mode 100644 index af693eb..0000000 --- a/src/main/java/haidnor/jvm/instruction/extended/IFNULL.java +++ /dev/null @@ -1,33 +0,0 @@ -package haidnor.jvm.instruction.extended; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class IFNULL extends Instruction { - /** - * 下次再执行的偏移量 - */ - private final int offSet; - - public IFNULL(CodeStream codeStream) { - super(codeStream); - this.offSet = codeStream.readShort(this); - } - - @Override - public void execute(Frame frame) { - StackValue v1 = frame.pop(); - if (v1.getValue() == null) { - super.setOffSet(offSet); - } else { - super.setOffSet(3); - } - } - - @Override - public String toString() { - return super.index() + " " + this.getClass().getSimpleName() + " " + offSet; - } -} diff --git a/src/main/java/haidnor/jvm/instruction/extended/MULTIANEWARRAY.java b/src/main/java/haidnor/jvm/instruction/extended/MULTIANEWARRAY.java deleted file mode 100644 index 1833c97..0000000 --- a/src/main/java/haidnor/jvm/instruction/extended/MULTIANEWARRAY.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.instruction.extended; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class MULTIANEWARRAY extends Instruction { - - public final int index; - public final int dimensions; - - public MULTIANEWARRAY(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedShort(this); - this.dimensions = codeStream.readUnsignedByte(this); - - throw new UnsupportedOperationException("MULTIANEWARRAY"); - } - - @Override - public void execute(Frame frame) { - throw new UnsupportedOperationException("MULTIANEWARRAY"); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/extended/WIDE.java b/src/main/java/haidnor/jvm/instruction/extended/WIDE.java deleted file mode 100644 index bbe453c..0000000 --- a/src/main/java/haidnor/jvm/instruction/extended/WIDE.java +++ /dev/null @@ -1,40 +0,0 @@ -package haidnor.jvm.instruction.extended; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.instruction.loads.*; -import haidnor.jvm.instruction.math.IINC; -import haidnor.jvm.instruction.stores.*; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class WIDE extends Instruction { - - private final Instruction instruction; - - public WIDE(CodeStream codeStream) { - super(codeStream); - int wideOpcode = codeStream.readShort(this); - - switch (wideOpcode) { - case 0x84 -> instruction = new IINC(codeStream); - case 0x15 -> instruction = new ILOAD(codeStream); - case 0x17 -> instruction = new FLOAD(codeStream); - case 0x19 -> instruction = new ALOAD(codeStream); - case 0x16 -> instruction = new LLOAD(codeStream); - case 0x18 -> instruction = new DLOAD(codeStream); - case 0x36 -> instruction = new ISTORE(codeStream); - case 0x38 -> instruction = new FSTORE(codeStream); - case 0x3a -> instruction = new ASTORE(codeStream); - case 0x37 -> instruction = new LSTORE(codeStream); - case 0x39 -> instruction = new DSTORE(codeStream); - case 0xa9 -> throw new UnsupportedOperationException(); // ret, ignore - default -> throw new UnsupportedOperationException(); - } - } - - @Override - public void execute(Frame frame) { - instruction.execute(frame); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/AALOAD.java b/src/main/java/haidnor/jvm/instruction/loads/AALOAD.java deleted file mode 100644 index 38b95ee..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/AALOAD.java +++ /dev/null @@ -1,24 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Instance; -import haidnor.jvm.rtda.InstanceArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - - -public class AALOAD extends Instruction { - - public AALOAD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int index = frame.popInt(); - InstanceArray array = (InstanceArray) frame.popRef(); - Instance item = (Instance) array.items[index]; - frame.pushRef(item); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/ALOAD.java b/src/main/java/haidnor/jvm/instruction/loads/ALOAD.java deleted file mode 100644 index 6dfa3d7..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/ALOAD.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - - -public class ALOAD extends Instruction { - - private final int index; - - public ALOAD(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedByte(this); - } - - @Override - public void execute(Frame frame) { - Object value = frame.slotGetRef(index); - frame.push(new StackValue(Const.T_OBJECT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/ALOAD_0.java b/src/main/java/haidnor/jvm/instruction/loads/ALOAD_0.java deleted file mode 100644 index 4c22869..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/ALOAD_0.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class ALOAD_0 extends Instruction { - - public ALOAD_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - Object value = frame.slotGetRef(0); - frame.push(new StackValue(Const.T_OBJECT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/ALOAD_1.java b/src/main/java/haidnor/jvm/instruction/loads/ALOAD_1.java deleted file mode 100644 index 7ba5cbc..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/ALOAD_1.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class ALOAD_1 extends Instruction { - - public ALOAD_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - Object value = frame.slotGetRef(1); - frame.push(new StackValue(Const.T_OBJECT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/ALOAD_2.java b/src/main/java/haidnor/jvm/instruction/loads/ALOAD_2.java deleted file mode 100644 index 2f1867d..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/ALOAD_2.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class ALOAD_2 extends Instruction { - - public ALOAD_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - Object value = frame.slotGetRef(2); - frame.push(new StackValue(Const.T_OBJECT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/ALOAD_3.java b/src/main/java/haidnor/jvm/instruction/loads/ALOAD_3.java deleted file mode 100644 index 005ccc0..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/ALOAD_3.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class ALOAD_3 extends Instruction { - - public ALOAD_3(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - Object value = frame.slotGetRef(3); - frame.push(new StackValue(Const.T_OBJECT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/BALOAD.java b/src/main/java/haidnor/jvm/instruction/loads/BALOAD.java deleted file mode 100644 index 5e9bbf4..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/BALOAD.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - - -public class BALOAD extends Instruction { - - public BALOAD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int index = frame.popInt(); - BasicTypeArray array = (BasicTypeArray) frame.popRef(); - frame.pushInt(array.ints[index]); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/CALOAD.java b/src/main/java/haidnor/jvm/instruction/loads/CALOAD.java deleted file mode 100644 index 3ccc6a1..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/CALOAD.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - - -public class CALOAD extends Instruction { - - public CALOAD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int index = frame.popInt(); - BasicTypeArray array = (BasicTypeArray) frame.popRef(); - frame.pushInt(array.ints[index]); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/DALOAD.java b/src/main/java/haidnor/jvm/instruction/loads/DALOAD.java deleted file mode 100644 index 5db952e..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/DALOAD.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - - -public class DALOAD extends Instruction { - - public DALOAD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int index = frame.popInt(); - BasicTypeArray array = (BasicTypeArray) frame.popRef(); - frame.pushDouble(array.doubles[index]); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/DLOAD.java b/src/main/java/haidnor/jvm/instruction/loads/DLOAD.java deleted file mode 100644 index 35a1235..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/DLOAD.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - - -public class DLOAD extends Instruction { - - private final int index; - - public DLOAD(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedByte(this); - } - - @Override - public void execute(Frame frame) { - double value = frame.slotGetDouble(index); - frame.push(new StackValue(Const.T_DOUBLE, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/DLOAD_0.java b/src/main/java/haidnor/jvm/instruction/loads/DLOAD_0.java deleted file mode 100644 index 5e39063..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/DLOAD_0.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class DLOAD_0 extends Instruction { - - public DLOAD_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - double value = frame.slotGetDouble(0); - frame.push(new StackValue(Const.T_DOUBLE, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/DLOAD_1.java b/src/main/java/haidnor/jvm/instruction/loads/DLOAD_1.java deleted file mode 100644 index 7cbb195..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/DLOAD_1.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class DLOAD_1 extends Instruction { - - public DLOAD_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - double value = frame.slotGetDouble(1); - frame.push(new StackValue(Const.T_DOUBLE, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/DLOAD_2.java b/src/main/java/haidnor/jvm/instruction/loads/DLOAD_2.java deleted file mode 100644 index 1648b66..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/DLOAD_2.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class DLOAD_2 extends Instruction { - - public DLOAD_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - double value = frame.slotGetDouble(2); - frame.push(new StackValue(Const.T_DOUBLE, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/DLOAD_3.java b/src/main/java/haidnor/jvm/instruction/loads/DLOAD_3.java deleted file mode 100644 index f5405ce..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/DLOAD_3.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class DLOAD_3 extends Instruction { - - public DLOAD_3(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - double value = frame.slotGetDouble(3); - frame.push(new StackValue(Const.T_DOUBLE, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/FALOAD.java b/src/main/java/haidnor/jvm/instruction/loads/FALOAD.java deleted file mode 100644 index f6e21d1..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/FALOAD.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - - -public class FALOAD extends Instruction { - - public FALOAD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int index = frame.popInt(); - BasicTypeArray array = (BasicTypeArray) frame.popRef(); - frame.pushFloat(array.floats[index]); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/FLOAD.java b/src/main/java/haidnor/jvm/instruction/loads/FLOAD.java deleted file mode 100644 index 7ce6301..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/FLOAD.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - - -public class FLOAD extends Instruction { - - private final int index; - - public FLOAD(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedByte(this); - } - - @Override - public void execute(Frame frame) { - float value = frame.slotGetFloat(index); - frame.push(new StackValue(Const.T_FLOAT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/FLOAD_0.java b/src/main/java/haidnor/jvm/instruction/loads/FLOAD_0.java deleted file mode 100644 index 5a3ae65..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/FLOAD_0.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class FLOAD_0 extends Instruction { - - public FLOAD_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - float value = frame.slotGetFloat(0); - frame.push(new StackValue(Const.T_FLOAT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/FLOAD_1.java b/src/main/java/haidnor/jvm/instruction/loads/FLOAD_1.java deleted file mode 100644 index eb6aca2..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/FLOAD_1.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class FLOAD_1 extends Instruction { - - public FLOAD_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - float value = frame.slotGetFloat(1); - frame.push(new StackValue(Const.T_FLOAT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/FLOAD_2.java b/src/main/java/haidnor/jvm/instruction/loads/FLOAD_2.java deleted file mode 100644 index cc740f2..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/FLOAD_2.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class FLOAD_2 extends Instruction { - - public FLOAD_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - float value = frame.slotGetFloat(2); - frame.push(new StackValue(Const.T_FLOAT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/FLOAD_3.java b/src/main/java/haidnor/jvm/instruction/loads/FLOAD_3.java deleted file mode 100644 index d2ead70..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/FLOAD_3.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class FLOAD_3 extends Instruction { - - public FLOAD_3(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - float value = frame.slotGetFloat(3); - frame.push(new StackValue(Const.T_FLOAT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/IALOAD.java b/src/main/java/haidnor/jvm/instruction/loads/IALOAD.java deleted file mode 100644 index 4abd5d2..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/IALOAD.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - - -public class IALOAD extends Instruction { - - public IALOAD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int index = frame.popInt(); - BasicTypeArray array = (BasicTypeArray) frame.popRef(); - frame.pushInt(array.ints[index]); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/ILOAD.java b/src/main/java/haidnor/jvm/instruction/loads/ILOAD.java deleted file mode 100644 index 78c5304..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/ILOAD.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - - -public class ILOAD extends Instruction { - - private final int index; - - public ILOAD(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedByte(this); - } - - @Override - public void execute(Frame frame) { - int value = frame.slotGetInt(index); - frame.push(new StackValue(Const.T_INT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/ILOAD_0.java b/src/main/java/haidnor/jvm/instruction/loads/ILOAD_0.java deleted file mode 100644 index 4396c21..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/ILOAD_0.java +++ /dev/null @@ -1,24 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.iload_n - */ -public class ILOAD_0 extends Instruction { - - public ILOAD_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int value = frame.slotGetInt(0); - frame.push(new StackValue(Const.T_INT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/ILOAD_1.java b/src/main/java/haidnor/jvm/instruction/loads/ILOAD_1.java deleted file mode 100644 index 9ea6009..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/ILOAD_1.java +++ /dev/null @@ -1,24 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.iload_n - */ -public class ILOAD_1 extends Instruction { - - public ILOAD_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int value = frame.slotGetInt(1); - frame.push(new StackValue(Const.T_INT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/ILOAD_2.java b/src/main/java/haidnor/jvm/instruction/loads/ILOAD_2.java deleted file mode 100644 index e8a00a4..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/ILOAD_2.java +++ /dev/null @@ -1,24 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.iload_n - */ -public class ILOAD_2 extends Instruction { - - public ILOAD_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int value = frame.slotGetInt(2); - frame.push(new StackValue(Const.T_INT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/ILOAD_3.java b/src/main/java/haidnor/jvm/instruction/loads/ILOAD_3.java deleted file mode 100644 index 6adc81a..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/ILOAD_3.java +++ /dev/null @@ -1,24 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -/** - * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.iload_n - */ -public class ILOAD_3 extends Instruction { - - public ILOAD_3(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int value = frame.slotGetInt(3); - frame.push(new StackValue(Const.T_INT, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/LALOAD.java b/src/main/java/haidnor/jvm/instruction/loads/LALOAD.java deleted file mode 100644 index ade4a89..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/LALOAD.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - - -public class LALOAD extends Instruction { - - public LALOAD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int index = frame.popInt(); - BasicTypeArray array = (BasicTypeArray) frame.popRef(); - frame.pushLong(array.longs[index]); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/LLOAD.java b/src/main/java/haidnor/jvm/instruction/loads/LLOAD.java deleted file mode 100644 index 702139d..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/LLOAD.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - - -public class LLOAD extends Instruction { - - private final int index; - - public LLOAD(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedByte(this); - } - - @Override - public void execute(Frame frame) { - long value = frame.slotGetLong(index); - frame.push(new StackValue(Const.T_LONG, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/LLOAD_0.java b/src/main/java/haidnor/jvm/instruction/loads/LLOAD_0.java deleted file mode 100644 index 2a7a29d..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/LLOAD_0.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class LLOAD_0 extends Instruction { - - public LLOAD_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - long value = frame.slotGetLong(0); - frame.push(new StackValue(Const.T_LONG, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/LLOAD_1.java b/src/main/java/haidnor/jvm/instruction/loads/LLOAD_1.java deleted file mode 100644 index 00b07e6..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/LLOAD_1.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class LLOAD_1 extends Instruction { - - public LLOAD_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - long value = frame.slotGetLong(1); - frame.push(new StackValue(Const.T_LONG, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/LLOAD_2.java b/src/main/java/haidnor/jvm/instruction/loads/LLOAD_2.java deleted file mode 100644 index acb724f..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/LLOAD_2.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class LLOAD_2 extends Instruction { - - public LLOAD_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - long value = frame.slotGetLong(2); - frame.push(new StackValue(Const.T_LONG, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/LLOAD_3.java b/src/main/java/haidnor/jvm/instruction/loads/LLOAD_3.java deleted file mode 100644 index 0c831fe..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/LLOAD_3.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class LLOAD_3 extends Instruction { - - public LLOAD_3(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - long value = frame.slotGetLong(3); - frame.push(new StackValue(Const.T_LONG, value)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/loads/SALOAD.java b/src/main/java/haidnor/jvm/instruction/loads/SALOAD.java deleted file mode 100644 index a51a2fa..0000000 --- a/src/main/java/haidnor/jvm/instruction/loads/SALOAD.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.loads; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - - -public class SALOAD extends Instruction { - - public SALOAD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int index = frame.popInt(); - BasicTypeArray array = (BasicTypeArray) frame.popRef(); - frame.pushInt(array.ints[index]); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/DADD.java b/src/main/java/haidnor/jvm/instruction/math/DADD.java deleted file mode 100644 index cd0f3d5..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/DADD.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class DADD extends Instruction { - - public DADD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - double result = (double) value1.getValue() + (double) value2.getValue(); - frame.push(new StackValue(Const.T_DOUBLE, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/DDIV.java b/src/main/java/haidnor/jvm/instruction/math/DDIV.java deleted file mode 100644 index 7db3560..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/DDIV.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class DDIV extends Instruction { - - public DDIV(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - double result = (double) value1.getValue() / (double) value2.getValue(); - frame.push(new StackValue(Const.T_DOUBLE, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/DMUL.java b/src/main/java/haidnor/jvm/instruction/math/DMUL.java deleted file mode 100644 index 0b66ca9..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/DMUL.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class DMUL extends Instruction { - - public DMUL(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - double result = (double) value1.getValue() * (double) value2.getValue(); - frame.push(new StackValue(Const.T_DOUBLE, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/DNEG.java b/src/main/java/haidnor/jvm/instruction/math/DNEG.java deleted file mode 100644 index efd5b75..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/DNEG.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class DNEG extends Instruction { - - public DNEG(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue stackValue = frame.pop(); - double value = (double) stackValue.getValue(); - double tmp = -value; - frame.push(new StackValue(Const.T_DOUBLE, tmp)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/DREM.java b/src/main/java/haidnor/jvm/instruction/math/DREM.java deleted file mode 100644 index 2fc29de..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/DREM.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class DREM extends Instruction { - - public DREM(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - double result = (double) value1.getValue() % (double) value2.getValue(); - frame.push(new StackValue(Const.T_DOUBLE, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/DSUB.java b/src/main/java/haidnor/jvm/instruction/math/DSUB.java deleted file mode 100644 index d25ff6f..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/DSUB.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class DSUB extends Instruction { - - public DSUB(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - double result = (double) value1.getValue() - (double) value2.getValue(); - frame.push(new StackValue(Const.T_DOUBLE, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/FADD.java b/src/main/java/haidnor/jvm/instruction/math/FADD.java deleted file mode 100644 index c4a73c4..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/FADD.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class FADD extends Instruction { - - public FADD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - float result = (float) value1.getValue() + (float) value2.getValue(); - frame.push(new StackValue(Const.T_FLOAT, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/FDIV.java b/src/main/java/haidnor/jvm/instruction/math/FDIV.java deleted file mode 100644 index 6e7e768..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/FDIV.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class FDIV extends Instruction { - - public FDIV(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - float result = (float) value1.getValue() / (float) value2.getValue(); - frame.push(new StackValue(Const.T_FLOAT, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/FMUL.java b/src/main/java/haidnor/jvm/instruction/math/FMUL.java deleted file mode 100644 index 7c82541..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/FMUL.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class FMUL extends Instruction { - - public FMUL(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - float result = (float) value1.getValue() * (float) value2.getValue(); - frame.push(new StackValue(Const.T_FLOAT, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/FNEG.java b/src/main/java/haidnor/jvm/instruction/math/FNEG.java deleted file mode 100644 index e245a33..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/FNEG.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class FNEG extends Instruction { - - public FNEG(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue stackValue = frame.pop(); - float value = (float) stackValue.getValue(); - float tmp = -value; - frame.push(new StackValue(Const.T_FLOAT, tmp)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/FREM.java b/src/main/java/haidnor/jvm/instruction/math/FREM.java deleted file mode 100644 index 280de40..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/FREM.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class FREM extends Instruction { - - public FREM(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - float result = (float) value1.getValue() % (float) value2.getValue(); - frame.push(new StackValue(Const.T_FLOAT, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/FSUB.java b/src/main/java/haidnor/jvm/instruction/math/FSUB.java deleted file mode 100644 index 0b83e45..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/FSUB.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class FSUB extends Instruction { - - public FSUB(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - float result = (float) value1.getValue() - (float) value2.getValue(); - frame.push(new StackValue(Const.T_FLOAT, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/IADD.java b/src/main/java/haidnor/jvm/instruction/math/IADD.java deleted file mode 100644 index d6c311f..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/IADD.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class IADD extends Instruction { - - public IADD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - int result = (int) value1.getValue() + (int) value2.getValue(); - frame.push(new StackValue(Const.T_INT, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/IAND.java b/src/main/java/haidnor/jvm/instruction/math/IAND.java deleted file mode 100644 index 396420f..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/IAND.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class IAND extends Instruction { - - public IAND(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - Integer v2 = frame.popInt(); - Integer v1 = frame.popInt(); - int val = v1 & v2; - frame.pushInt(val); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/IDIV.java b/src/main/java/haidnor/jvm/instruction/math/IDIV.java deleted file mode 100644 index fb2e53c..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/IDIV.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class IDIV extends Instruction { - - public IDIV(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - int result = (int) value1.getValue() / (int) value2.getValue(); - frame.push(new StackValue(Const.T_INT, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/IINC.java b/src/main/java/haidnor/jvm/instruction/math/IINC.java deleted file mode 100644 index 8dd1958..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/IINC.java +++ /dev/null @@ -1,37 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -/** - * Java VM opcode. - * - * @see Opcode definitions in - * The Java Virtual Machine Specification - */ -public class IINC extends Instruction { - - /** - * 局部变量表中需要被自增元素的索引 - */ - public final int index; - - /** - * 自增值 - */ - public final int increment; - - public IINC(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedByte(this); - this.increment = codeStream.readByte(this); - } - - @Override - public void execute(Frame frame) { - int value = frame.slotGetInt(index); - frame.slotSetInt(index, value + increment); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/IMUL.java b/src/main/java/haidnor/jvm/instruction/math/IMUL.java deleted file mode 100644 index 6ffaed8..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/IMUL.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class IMUL extends Instruction { - - public IMUL(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - int result = (int) value1.getValue() * (int) value2.getValue(); - frame.push(new StackValue(Const.T_INT, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/INEG.java b/src/main/java/haidnor/jvm/instruction/math/INEG.java deleted file mode 100644 index 6e2c073..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/INEG.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class INEG extends Instruction { - - public INEG(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue stackValue = frame.pop(); - int value = (int) stackValue.getValue(); - int tmp = -value; - frame.push(new StackValue(Const.T_INT, tmp)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/IOR.java b/src/main/java/haidnor/jvm/instruction/math/IOR.java deleted file mode 100644 index 0202bb3..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/IOR.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class IOR extends Instruction { - - public IOR(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int v2 = frame.popInt(); - int v1 = frame.popInt(); - frame.pushInt(v1 | v2); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/IREM.java b/src/main/java/haidnor/jvm/instruction/math/IREM.java deleted file mode 100644 index 62f6ae5..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/IREM.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class IREM extends Instruction { - - public IREM(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - int result = (int) value1.getValue() % (int) value2.getValue(); - frame.push(new StackValue(Const.T_INT, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/ISHL.java b/src/main/java/haidnor/jvm/instruction/math/ISHL.java deleted file mode 100644 index d9273b5..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/ISHL.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class ISHL extends Instruction { - - public ISHL(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int v2 = frame.popInt(); - int v1 = frame.popInt(); - int s = v2 & 0x1f; - int ret = v1 << s; - frame.pushInt(ret); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/ISHR.java b/src/main/java/haidnor/jvm/instruction/math/ISHR.java deleted file mode 100644 index ae36c0d..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/ISHR.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class ISHR extends Instruction { - - public ISHR(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int v2 = frame.popInt(); - int v1 = frame.popInt(); - int s = v2 & 0x1f; - int ret = v1 >> s; - frame.pushInt(ret); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/ISUB.java b/src/main/java/haidnor/jvm/instruction/math/ISUB.java deleted file mode 100644 index 8e39ccd..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/ISUB.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class ISUB extends Instruction { - - public ISUB(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - int result = (int) value1.getValue() - (int) value2.getValue(); - frame.push(new StackValue(Const.T_INT, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/IUSHR.java b/src/main/java/haidnor/jvm/instruction/math/IUSHR.java deleted file mode 100644 index 7f0c822..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/IUSHR.java +++ /dev/null @@ -1,37 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -/** - * JVM中的IUSHR指令是用于执行无符号右移操作的指令。该指令将两个整数值从操作数栈中弹出, - * 然后将第一个操作数的位表示向右移动第二个操作数指定的位数,移位过程中高位用零填充。最后,将结果压入操作数栈中。 - * - * 在Java虚拟机规范中,IUSHR指令的操作码为0x7C,它属于逻辑指令家族(logical instructions)。 - * 这个指令通常需用到无符号数值在位级上的运算,因此它主要用于一些特定的计算和算法中。 - * - * 需要注意的是,IUSHR指令只适用于int类型的数据。如果操作数是long类型,则需要使用LUSHR指令进行无符号右移操作。 - */ -public class IUSHR extends Instruction { - - public IUSHR(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int v2 = frame.popInt(); - int v1 = frame.popInt(); - int s = v2 & 0x1f; - - if (v1 >= 0) { - int ret = v1 >> s; - frame.pushInt(ret); - return; - } - int ret = (v1 >> s) + (2 << ~s); - frame.pushInt(ret); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/IXOR.java b/src/main/java/haidnor/jvm/instruction/math/IXOR.java deleted file mode 100644 index f6aceba..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/IXOR.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class IXOR extends Instruction { - - public IXOR(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - final int v2 = frame.popInt(); - final int v1 = frame.popInt(); - frame.pushInt(v1 ^ v2); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LADD.java b/src/main/java/haidnor/jvm/instruction/math/LADD.java deleted file mode 100644 index 06bf035..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LADD.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class LADD extends Instruction { - - public LADD(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - long result = (long) value1.getValue() + (long) value2.getValue(); - frame.push(new StackValue(Const.T_LONG, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LAND.java b/src/main/java/haidnor/jvm/instruction/math/LAND.java deleted file mode 100644 index 06988b9..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LAND.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class LAND extends Instruction { - - public LAND(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - long a1 = frame.popLong(); - long a2 = frame.popLong(); - frame.pushLong(a2 & a1); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LDIV.java b/src/main/java/haidnor/jvm/instruction/math/LDIV.java deleted file mode 100644 index fbcf6e6..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LDIV.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class LDIV extends Instruction { - - public LDIV(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - long result = (long) value1.getValue() / (long) value2.getValue(); - frame.push(new StackValue(Const.T_LONG, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LMUL.java b/src/main/java/haidnor/jvm/instruction/math/LMUL.java deleted file mode 100644 index 6b448eb..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LMUL.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class LMUL extends Instruction { - - public LMUL(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - long result = (long) value1.getValue() * (long) value2.getValue(); - frame.push(new StackValue(Const.T_LONG, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LNEG.java b/src/main/java/haidnor/jvm/instruction/math/LNEG.java deleted file mode 100644 index 385f17b..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LNEG.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class LNEG extends Instruction { - - public LNEG(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue stackValue = frame.pop(); - long value = (long) stackValue.getValue(); - long tmp = -value; - frame.push(new StackValue(Const.T_LONG, tmp)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LOR.java b/src/main/java/haidnor/jvm/instruction/math/LOR.java deleted file mode 100644 index 024a15d..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LOR.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class LOR extends Instruction { - - public LOR(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - Long v2 = frame.popLong(); - Long v1 = frame.popLong(); - frame.pushLong(v1 | v2); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LREM.java b/src/main/java/haidnor/jvm/instruction/math/LREM.java deleted file mode 100644 index f3afcf4..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LREM.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class LREM extends Instruction { - - public LREM(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - long result = (long) value1.getValue() % (long) value2.getValue(); - frame.push(new StackValue(Const.T_LONG, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LSHL.java b/src/main/java/haidnor/jvm/instruction/math/LSHL.java deleted file mode 100644 index 8177ad2..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LSHL.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class LSHL extends Instruction { - - public LSHL(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int v2 = frame.popInt(); - long v1 = frame.popLong(); - int s = v2 & 0x1f; - long ret = v1 << s; - frame.pushLong(ret); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LSHR.java b/src/main/java/haidnor/jvm/instruction/math/LSHR.java deleted file mode 100644 index 7837209..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LSHR.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class LSHR extends Instruction { - - public LSHR(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - final int v2 = frame.popInt(); - final long v1 = frame.popLong(); - frame.pushLong(v1 >> v2); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LSUB.java b/src/main/java/haidnor/jvm/instruction/math/LSUB.java deleted file mode 100644 index e4f4229..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LSUB.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import org.apache.bcel.Const; - -public class LSUB extends Instruction { - - public LSUB(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value2 = frame.pop(); - StackValue value1 = frame.pop(); - long result = (long) value1.getValue() - (long) value2.getValue(); - frame.push(new StackValue(Const.T_LONG, result)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LUSHR.java b/src/main/java/haidnor/jvm/instruction/math/LUSHR.java deleted file mode 100644 index fbe63a9..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LUSHR.java +++ /dev/null @@ -1,28 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class LUSHR extends Instruction { - - public LUSHR(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int v2 = frame.popInt(); - long v1 = frame.popLong(); - int s = v2 & 0x3f; - - if (v1 >= 0) { - long ret = v1 >> s; - frame.pushLong(ret); - return; - } - long ret = (v1 >> s) + (2L << ~s); - frame.pushLong(ret); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/math/LXOR.java b/src/main/java/haidnor/jvm/instruction/math/LXOR.java deleted file mode 100644 index cb13220..0000000 --- a/src/main/java/haidnor/jvm/instruction/math/LXOR.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.math; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class LXOR extends Instruction { - - public LXOR(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - long v2 = frame.popLong(); - long v1 = frame.popLong(); - frame.pushLong(v1 ^ v2); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/ANEWARRAY.java b/src/main/java/haidnor/jvm/instruction/references/ANEWARRAY.java deleted file mode 100644 index fc5c18c..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/ANEWARRAY.java +++ /dev/null @@ -1,41 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Instance; -import haidnor.jvm.rtda.InstanceArray; -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.Metaspace; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.Utility; - -public class ANEWARRAY extends Instruction { - - private final int constantClassIndex; - - public ANEWARRAY(CodeStream codeStream) { - super(codeStream); - this.constantClassIndex = codeStream.readUnsignedShort(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - String className = frame.getConstantPoolUtil().constantClass_ClassName(constantClassIndex); - - Klass klass = Metaspace.getJavaClass(Utility.compactClassName(className)); - if (klass == null) { - // 如果在元空间中找不到已加载的类,则开始进行类加载流程 - klass = frame.getMetaClass().getClassLoader().loadClass(className); - } - int size = frame.popInt(); - Instance[] items = new Instance[size]; - InstanceArray instanceArray = new InstanceArray(klass, items); - - frame.push(new StackValue(Const.T_OBJECT, instanceArray)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/ARRAYLENGTH.java b/src/main/java/haidnor/jvm/instruction/references/ARRAYLENGTH.java deleted file mode 100644 index 0b5ca55..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/ARRAYLENGTH.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.ArrayInstance; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -public class ARRAYLENGTH extends Instruction { - - public ARRAYLENGTH(CodeStream codeStream) { - super(codeStream); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ArrayInstance array = (ArrayInstance) frame.popRef(); - frame.pushInt(array.size); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/ATHROW.java b/src/main/java/haidnor/jvm/instruction/references/ATHROW.java deleted file mode 100644 index fc5a2c0..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/ATHROW.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -public class ATHROW extends Instruction { - - public ATHROW(CodeStream codeStream) { - super(codeStream); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - StackValue pop = frame.pop(); - throw (Exception) pop.getValue(); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/CHECKCAST.java b/src/main/java/haidnor/jvm/instruction/references/CHECKCAST.java deleted file mode 100644 index 15a4874..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/CHECKCAST.java +++ /dev/null @@ -1,24 +0,0 @@ - -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -public class CHECKCAST extends Instruction { - - private final int constantClassIndex; - - public CHECKCAST(CodeStream codeStream) { - super(codeStream); - this.constantClassIndex = codeStream.readUnsignedShort(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - // 暂时不支持 - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/GETFIELD.java b/src/main/java/haidnor/jvm/instruction/references/GETFIELD.java deleted file mode 100644 index ca4bfc5..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/GETFIELD.java +++ /dev/null @@ -1,52 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Instance; -import haidnor.jvm.rtda.KlassField; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.ConstantPoolUtil; -import lombok.SneakyThrows; - -public class GETFIELD extends Instruction { - - private final int constantFieldrefIndex; - - public GETFIELD(CodeStream codeStream) { - super(codeStream); - this.constantFieldrefIndex = codeStream.readUnsignedShort(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ConstantPoolUtil constantPool = frame.getConstantPoolUtil(); - String filedName = constantPool.getFieldName(constantFieldrefIndex); - String fieldSignature = constantPool.getFieldSignature(constantFieldrefIndex); - - Instance instanceRef = frame.popRef(); - KlassField field = instanceRef.getField(filedName, fieldSignature); - switch (field.descriptor) { - case "Z": - case "C": - case "B": - case "S": - case "I": - frame.pushInt((int) field.getValue()); - break; - case "J": - frame.pushLong((long) field.getValue()); - break; - case "F": - frame.pushFloat((float) field.getValue()); - break; - case "D": - frame.pushDouble((double) field.getValue()); - break; - default: // ref - frame.pushRef(field.getValue()); - break; - } - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/GETSTATIC.java b/src/main/java/haidnor/jvm/instruction/references/GETSTATIC.java deleted file mode 100644 index 156b7e5..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/GETSTATIC.java +++ /dev/null @@ -1,57 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.KlassField; -import haidnor.jvm.rtda.Metaspace; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.ConstantPoolUtil; -import lombok.SneakyThrows; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.ConstantFieldref; -import org.apache.bcel.classfile.ConstantPool; -import org.apache.bcel.classfile.Utility; - -import java.lang.reflect.Field; - -/** - * 获取字段符号引用指定的对象或者值(类的静态字段 static 修饰),并将其压入操作数栈 - * - * @author wang xiang - */ -public class GETSTATIC extends Instruction { - - private final int constantFieldrefIndex; - - public GETSTATIC(CodeStream codeStream) { - super(codeStream); - this.constantFieldrefIndex = codeStream.readUnsignedShort(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ConstantPool constantPool = frame.getConstantPool(); - ConstantPoolUtil constantPoolUtil = frame.getConstantPoolUtil(); - ConstantFieldref constFieldref = constantPool.getConstant(constantFieldrefIndex); - // 动态链接. 找到字段所属的 Java 类 - String className = constantPoolUtil.constantFieldref_ClassName(constFieldref); - // 动态链接. 找到字段的名字 - String fieldName = constantPoolUtil.getFieldName(constFieldref); - // 以上代码体现了动态链接.Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池里指向方法的符号引用作为参数. - - if (className.startsWith("java/")) { - Class clazz = Class.forName(className.replace('/', '.')); - Field field = clazz.getField(fieldName); - Object staticFiledValue = field.get(null); // 获取静态字段上的值 - frame.push(new StackValue(Const.T_OBJECT, staticFiledValue)); - } else { - Klass javaClass = Metaspace.getJavaClass(Utility.compactClassName(className)); - KlassField staticField = javaClass.getStaticField(fieldName); - frame.push(new StackValue(Const.T_OBJECT, staticField.getValue())); - } - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/INSTANCEOF.java b/src/main/java/haidnor/jvm/instruction/references/INSTANCEOF.java deleted file mode 100644 index c730dbc..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/INSTANCEOF.java +++ /dev/null @@ -1,78 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Instance; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.ConstantPoolUtil; -import lombok.SneakyThrows; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.JavaClass; -import org.apache.bcel.classfile.Utility; - -/** - * @author wang xiang - */ -public class INSTANCEOF extends Instruction { - - private final int constantClassIndex; - - public INSTANCEOF(CodeStream codeStream) { - super(codeStream); - this.constantClassIndex = codeStream.readUnsignedShort(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ConstantPoolUtil constantPoolUtil = frame.getConstantPoolUtil(); - - String className = constantPoolUtil.constantClass_ClassName(constantClassIndex); - className = Utility.compactClassName(className); - - StackValue stackValue = frame.pop(); - Object obj = stackValue.getValue(); - Class objClass = obj.getClass(); - - if (obj instanceof Instance instance) { - boolean result = findClassFromSuper(instance.klass.getJavaClass(), className); - if (!result) { - result = findClassFromInterface(instance.klass.getJavaClass(), className); - } - if (result) { - frame.push(new StackValue(Const.T_INT, 1)); - } else { - frame.push(new StackValue(Const.T_INT, 0)); - } - } else { - if (Class.forName(className).isAssignableFrom(objClass)) { - frame.push(new StackValue(Const.T_INT, 1)); - } else { - frame.push(new StackValue(Const.T_INT, 0)); - } - } - } - - public boolean findClassFromSuper(JavaClass javaClass, String className) throws ClassNotFoundException { - if (javaClass.getClassName().equals(className)) { - return true; - } - for (JavaClass superClass : javaClass.getSuperClasses()) { - if (superClass.getClassName().equals(className)) { - return true; - } - } - return false; - } - - public boolean findClassFromInterface(JavaClass javaClass, String className) throws ClassNotFoundException { - for (JavaClass anInterface : javaClass.getInterfaces()) { - if (anInterface.getClassName().equals(className)) { - return true; - } - } - return false; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/INVOKEDYNAMIC.java b/src/main/java/haidnor/jvm/instruction/references/INVOKEDYNAMIC.java deleted file mode 100644 index 5dc978a..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/INVOKEDYNAMIC.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -public class INVOKEDYNAMIC extends Instruction { - - public INVOKEDYNAMIC(CodeStream codeStream) { - super(codeStream); - throw new UnsupportedOperationException("INVOKEDYNAMIC"); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - throw new UnsupportedOperationException("INVOKEDYNAMIC"); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/INVOKEINTERFACE.java b/src/main/java/haidnor/jvm/instruction/references/INVOKEINTERFACE.java deleted file mode 100644 index 946d9e1..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/INVOKEINTERFACE.java +++ /dev/null @@ -1,155 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.classloader.ClassLoader; -import haidnor.jvm.core.JavaExecutionEngine; -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Instance; -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.KlassMethod; -import haidnor.jvm.rtda.Metaspace; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.ConstantPoolUtil; -import haidnor.jvm.util.SignatureUtil; -import lombok.SneakyThrows; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.ConstantInterfaceMethodref; -import org.apache.bcel.classfile.ConstantPool; -import org.apache.bcel.classfile.JavaClass; -import org.apache.bcel.classfile.Utility; - -import java.lang.reflect.Method; -import java.util.Objects; - - -public class INVOKEINTERFACE extends Instruction { - - private final int constantMethodrefIndex; - - public final int count; - - public final int zero; - - public INVOKEINTERFACE(CodeStream codeStream) { - super(codeStream); - this.constantMethodrefIndex = codeStream.readUnsignedShort(this); - this.count = codeStream.readUnsignedByte(this); - this.zero = codeStream.readUnsignedByte(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ConstantPool constantPool = frame.getConstantPool(); - ConstantPoolUtil constantPoolUtil = frame.getConstantPoolUtil(); - - ConstantInterfaceMethodref interfaceMethodref = constantPool.getConstant(constantMethodrefIndex); - - int classIndex = interfaceMethodref.getClassIndex(); - String interfaceName = constantPoolUtil.constantClass_ClassName(classIndex); - - int nameAndTypeIndex = interfaceMethodref.getNameAndTypeIndex(); - String methodName = constantPoolUtil.constantNameAndType_name(nameAndTypeIndex); - String methodSignature = constantPoolUtil.constantNameAndType_signature(nameAndTypeIndex); - - // 系统类反射 自定义类另外处理 - if (interfaceName.startsWith("java/")) { - // 解析方法签名得到方法的返回类型 - String returnType = Utility.methodSignatureReturnType(methodSignature, false); - // 执行方法的参数列表 - Class[] parameterTypeArr = SignatureUtil.getParameterTypes(methodSignature); - // 执行方法的参数值 - Object[] args = frame.popStacksValue(parameterTypeArr.length); - // 将特定的参数转换为基本类型 - for (int i = 0; i < parameterTypeArr.length; i++) { - Class clazz = parameterTypeArr[i]; - if (clazz.getName().equals("boolean")) { // boolean 存储方式为 int 类型 - int booleanFlag = (int) args[i]; - args[i] = booleanFlag == 1; - } else if (clazz.getName().equals("char")) { // char 存储方式为 - int charInt = (int) args[i]; - char c = (char) charInt; - args[i] = c; - } - } - - StackValue stackValue = frame.pop(); - Object obj = stackValue.getValue(); - Method method = obj.getClass().getMethod(methodName, parameterTypeArr); - method.setAccessible(true); - Object value = method.invoke(obj, args); - if (!Objects.equals(Const.getTypeName(Const.T_VOID), returnType)) { // void 调用的方法无返回值 - frame.push(new StackValue(Const.T_OBJECT, value)); - } - } - // 调用自定义类的方法 - else { - Klass interfaceKlass = Metaspace.getJavaClass(Utility.compactClassName(interfaceName)); - if (interfaceKlass == null) { - ClassLoader classLoader = frame.getMetaClass().getClassLoader(); - classLoader.loadClass(interfaceName); - } - - StackValue stackValue = frame.peek(); - Instance instance = (Instance) stackValue.getValue(); - Klass klass = instance.klass; - - // 按照继承关系从下往上找实现的方法 (实现多态) - org.apache.bcel.classfile.Method method = getMethod(klass.getJavaClass(), methodSignature, methodName); - // 从接口找方法 JDK8. interface default() - if (method == null) { - method = getMethodFromInterface(klass.getJavaClass(), methodSignature, methodName); - } - if (method == null) { - throw new AbstractMethodError(); - } - KlassMethod klassMethod = new KlassMethod(klass, method); - JavaExecutionEngine.callMethod(frame, klassMethod); - } - } - - /** - * 递归查找方法, 如果子类没有实现方法则向父类查找 - */ - private static org.apache.bcel.classfile.Method getMethod(JavaClass javaClass, String methodSignature, String methodName) throws ClassNotFoundException { - org.apache.bcel.classfile.Method m = null; - for (org.apache.bcel.classfile.Method method : javaClass.getMethods()) { - if (method.getSignature().equals(methodSignature) && method.getName().equals(methodName)) { - m = method; - } - } - if (m != null) { - return m; - } - if (javaClass.getSuperClass() == null) { - return null; - } - return getMethod(javaClass.getSuperClass(), methodSignature, methodName); - } - - private static org.apache.bcel.classfile.Method getMethodFromInterface(JavaClass javaClass, String methodSignature, String methodName) throws ClassNotFoundException { - JavaClass[] interfaces = javaClass.getInterfaces(); - if (interfaces.length == 0) { - for (org.apache.bcel.classfile.Method method : javaClass.getMethods()) { - if (method.getSignature().equals(methodSignature) && method.getName().equals(methodName)) { - return method; - } - } - } - for (JavaClass interfaceJavaClass : interfaces) { - for (org.apache.bcel.classfile.Method method : interfaceJavaClass.getMethods()) { - if (method.getSignature().equals(methodSignature) && method.getName().equals(methodName)) { - return method; - } - } - if (interfaceJavaClass.getInterfaces() != null) { - for (JavaClass classInterface : interfaceJavaClass.getInterfaces()) { - return getMethodFromInterface(classInterface, methodSignature, methodName); - } - } - } - return null; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/INVOKESPECIAL.java b/src/main/java/haidnor/jvm/instruction/references/INVOKESPECIAL.java deleted file mode 100644 index 80996fa..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/INVOKESPECIAL.java +++ /dev/null @@ -1,87 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.classloader.ClassLoader; -import haidnor.jvm.core.JavaExecutionEngine; -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.KlassMethod; -import haidnor.jvm.rtda.Metaspace; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.ConstantPoolUtil; -import haidnor.jvm.util.SignatureUtil; -import lombok.SneakyThrows; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.*; - -/** - * 调用一些需要特殊处理的实例方法,包括构造器方法,私有方法,父类方法,这些方法都是静态绑定的,不会在调用时进行动态分配 - */ -public class INVOKESPECIAL extends Instruction { - - private final int constantMethodrefIndex; - - public INVOKESPECIAL(CodeStream codeStream) { - super(codeStream); - this.constantMethodrefIndex = codeStream.readUnsignedShort(this); - } - - @SneakyThrows - @Override - public void execute(Frame frame) { - ConstantPool constantPool = frame.getConstantPool(); - ConstantPoolUtil constantPoolUtil = frame.getConstantPoolUtil(); - ConstantMethodref methodref = constantPool.getConstant(constantMethodrefIndex); - - String className = constantPoolUtil.constantMethodref_ClassName(methodref); - String methodName = constantPoolUtil.constantMethodref_MethodName(methodref); - String methodSignature = constantPoolUtil.constantMethodref_MethodSignature(methodref); - - Klass klass = Metaspace.getJavaClass(Utility.compactClassName(className)); - JavaClass javaClass; - if (klass != null) { - javaClass = klass.getJavaClass(); - } else { - ClassLoader classLoader = frame.getMetaClass().getClassLoader(); - klass = classLoader.loadClass(className); - javaClass = klass.getJavaClass(); - } - - if (className.startsWith("java/")) { - // 执行 RT.jar 中 Java 对象构造方法的时候创建java对象 - if (methodName.equals("")) { - // 执行方法的参数列表 - Class[] parameterTypeArr = SignatureUtil.getParameterTypes(methodSignature); - // 执行方法的参数值 - Object[] args = frame.popStacksValue(parameterTypeArr.length); - // 将特定的参数转换为基本类型 - for (int i = 0; i < parameterTypeArr.length; i++) { - Class clazz = parameterTypeArr[i]; - if (clazz.getName().equals("boolean")) { // boolean 存储方式为 int 类型 - int booleanFlag = (int) args[i]; - args[i] = booleanFlag == 1; - } else if (clazz.getName().equals("char")) { // char 存储方式为 - int charInt = (int) args[i]; - char c = (char) charInt; - args[i] = c; - } - } - Object javaObj = Class.forName(Utility.compactClassName(className,false)).getDeclaredConstructor(parameterTypeArr).newInstance(args); - frame.push(new StackValue(Const.T_OBJECT, javaObj)); // NEW - frame.push(new StackValue(Const.T_OBJECT, javaObj)); // DUP - return; - } - } - - for (Method method : javaClass.getMethods()) { - if (method.getSignature().equals(methodSignature) && method.getName().equals(methodName)) { - KlassMethod klassMethod = new KlassMethod(klass, method); - JavaExecutionEngine.callMethod(frame, klassMethod); - break; - } - } - } - - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/INVOKESTATIC.java b/src/main/java/haidnor/jvm/instruction/references/INVOKESTATIC.java deleted file mode 100644 index f766e19..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/INVOKESTATIC.java +++ /dev/null @@ -1,97 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.core.JavaExecutionEngine; -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.KlassMethod; -import haidnor.jvm.rtda.Metaspace; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.ConstantPoolUtil; -import haidnor.jvm.util.SignatureUtil; -import lombok.SneakyThrows; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.*; - -import java.util.Objects; - -/** - * 调用 static 静态方法.这是静态绑定的 - */ -public class INVOKESTATIC extends Instruction { - - private final int constantIndex; - - public INVOKESTATIC(CodeStream codeStream) { - super(codeStream); - this.constantIndex = codeStream.readUnsignedShort(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ConstantPool constantPool = frame.getConstantPool(); - ConstantPoolUtil constantPoolUtil = frame.getConstantPoolUtil(); - - String className; - String methodName; - String methodSignature; - - Constant constant = constantPool.getConstant(constantIndex); - if (constant instanceof ConstantMethodref constantMethodref) { - className = constantPoolUtil.constantMethodref_ClassName(constantMethodref); - methodName = constantPoolUtil.constantMethodref_MethodName(constantMethodref); - methodSignature = constantPoolUtil.constantMethodref_MethodSignature(constantMethodref); - - } else if (constant instanceof ConstantInterfaceMethodref interfaceMethodref) { - className = constantPoolUtil.constantInterfaceMethodref_ClassName(interfaceMethodref); - methodName = constantPoolUtil.constantInterfaceMethodref_MethodName(interfaceMethodref); - methodSignature = constantPoolUtil.constantInterfaceMethodref_MethodSignature(interfaceMethodref); - } else { - throw new ClassCastException(); - } - - // 系统类反射 自定义类另外处理 - if (className.startsWith("java/")) { - // 解析方法签名得到方法的返回类型 - String returnType = Utility.methodSignatureReturnType(methodSignature, false); - java.lang.Class[] parameterTypeArr = SignatureUtil.getParameterTypes(methodSignature); - Object[] stacksValueArr = frame.popStacksValue(parameterTypeArr.length); - for (int i = 0; i < parameterTypeArr.length; i++) { - java.lang.Class aClass = parameterTypeArr[i]; - if (aClass.getName().equals("boolean")) { - int booleanFlag = (int) stacksValueArr[i]; - stacksValueArr[i] = booleanFlag == 1; - } - } - - Class javaClass = Class.forName(Utility.compactClassName(className, false)); - java.lang.reflect.Method method = javaClass.getMethod(methodName, parameterTypeArr); - method.setAccessible(true); - Object value = method.invoke(null, stacksValueArr); - if (!Objects.equals(Const.getTypeName(Const.T_VOID), returnType)) { // void 调用的方法无返回值 - frame.push(new StackValue(Const.T_OBJECT, value)); - } - } - // 自定义类的方法 - else { - Klass klass = Metaspace.getJavaClass(Utility.compactClassName(className)); - // 调用静态方法时加载类 - if (klass == null) { - klass = frame.getMetaClass().getClassLoader().loadClass(className); - } - JavaClass javaClass = klass.getJavaClass(); - for (Method method : javaClass.getMethods()) { - if (method.getSignature().equals(methodSignature) && method.getName().equals(methodName)) { - KlassMethod klassMethod = new KlassMethod(klass, method); - JavaExecutionEngine.callMethod(frame, klassMethod); - break; - } - } - - } - - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/INVOKEVIRTUAL.java b/src/main/java/haidnor/jvm/instruction/references/INVOKEVIRTUAL.java deleted file mode 100644 index 23d58ee..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/INVOKEVIRTUAL.java +++ /dev/null @@ -1,119 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.classloader.ClassLoader; -import haidnor.jvm.core.JavaExecutionEngine; -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.KlassMethod; -import haidnor.jvm.rtda.Metaspace; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.ConstantPoolUtil; -import haidnor.jvm.util.SignatureUtil; -import lombok.SneakyThrows; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.ConstantMethodref; -import org.apache.bcel.classfile.ConstantPool; -import org.apache.bcel.classfile.JavaClass; -import org.apache.bcel.classfile.Utility; - -import java.lang.reflect.Method; -import java.util.Objects; - -/** - * 调用对象实例方法,根据对象的实际类型进行分派(虚方法分派),支持多态 - */ -public class INVOKEVIRTUAL extends Instruction { - - private final int constantMethodrefIndex; - - public INVOKEVIRTUAL(CodeStream codeStream) { - super(codeStream); - this.constantMethodrefIndex = codeStream.readUnsignedShort(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ConstantPool constantPool = frame.getConstantPool(); - ConstantPoolUtil constantPoolUtil = frame.getConstantPoolUtil(); - - ConstantMethodref methodref = constantPool.getConstant(constantMethodrefIndex); - - String className = constantPoolUtil.constantMethodref_ClassName(methodref); - String methodName = constantPoolUtil.constantMethodref_MethodName(methodref); - String methodSignature = constantPoolUtil.constantMethodref_MethodSignature(methodref); - - // 系统类反射 自定义类另外处理 - if (className.startsWith("java/")) { - // 解析方法签名得到方法的返回类型 - String returnType = Utility.methodSignatureReturnType(methodSignature, false); - // 执行方法的参数列表 - Class[] parameterTypeArr = SignatureUtil.getParameterTypes(methodSignature); - // 执行方法的参数值 - Object[] args = frame.popStacksValue(parameterTypeArr.length); - // 将特定的参数转换为基本类型 - for (int i = 0; i < parameterTypeArr.length; i++) { - Class clazz = parameterTypeArr[i]; - if (clazz.getName().equals("boolean")) { // boolean 存储方式为 int 类型 - int booleanFlag = (int) args[i]; - args[i] = booleanFlag == 1; - } else if (clazz.getName().equals("char")) { // char 存储方式为 - int charInt = (int) args[i]; - char c = (char) charInt; - args[i] = c; - } - } - - StackValue stackValue = frame.pop(); - Object obj = stackValue.getValue(); - Method method = obj.getClass().getMethod(methodName, parameterTypeArr); - method.setAccessible(true); - Object value = method.invoke(obj, args); - if (!Objects.equals(Const.getTypeName(Const.T_VOID), returnType)) { // void 调用的方法无返回值 - frame.push(new StackValue(Const.T_OBJECT, value)); - } - } - // 调用自定义类的方法 - else { - Klass klass = Metaspace.getJavaClass(Utility.compactClassName(className)); - JavaClass javaClass; - if (klass != null) { - javaClass = klass.getJavaClass(); - } else { - ClassLoader classLoader = frame.getMetaClass().getClassLoader(); - klass = classLoader.loadClass(className); - javaClass = klass.getJavaClass(); - } - - // 按照继承关系从下往上找实现的方法 (实现多态) - org.apache.bcel.classfile.Method method = getMethod(javaClass, methodSignature, methodName); - if (method == null) { - throw new AbstractMethodError(); - } - KlassMethod klassMethod = new KlassMethod(klass, method); - JavaExecutionEngine.callMethod(frame, klassMethod); - } - } - - /** - * 递归查找方法, 如果子类没有实现方法则向父类查找 - */ - private static org.apache.bcel.classfile.Method getMethod(JavaClass javaClass, String methodSignature,String methodName) throws ClassNotFoundException { - org.apache.bcel.classfile.Method m = null; - for (org.apache.bcel.classfile.Method method : javaClass.getMethods()) { - if (method.getSignature().equals(methodSignature) && method.getName().equals(methodName)) { - m = method; - } - } - if (m != null) { - return m; - } - if (javaClass.getSuperClass() == null) { - return null; - } - return getMethod(javaClass.getSuperClass(), methodSignature, methodName); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/JSR.java b/src/main/java/haidnor/jvm/instruction/references/JSR.java deleted file mode 100644 index eccb23c..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/JSR.java +++ /dev/null @@ -1,30 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -/** - * JSR(Jump to Subroutine)指令是一条在Java虚拟机中用于进行子程序调用的指令。它将当前指令的返回地址压入操作数栈,并跳转到指定的子程序代码执行。子程序执行完毕后,通过RET(Return)指令返回到主程序的下一条指令继续执行。 - *

- * JSR指令的操作码为0xA8,属于控制指令家族(Control Instructions)。它的操作数是一个16位的无符号偏移量,用于表示跳转目标的字节码偏移量。 - *

- * 使用JSR指令可以实现方法的调用和返回,但在实际的Java字节码中,更常见的是使用invokestatic、invokevirtual等指令来进行方法调用。 - *

- * 需要注意的是,JSR指令只在早期版本的Java虚拟机规范中存在,在Java SE 6及以后的版本中已被废弃,并且不建议在新的Java代码中使用。 - */ -public class JSR extends Instruction { - - public JSR(CodeStream codeStream) { - super(codeStream); - throw new UnsupportedOperationException("JSR"); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - throw new UnsupportedOperationException("JSR"); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/JSR_W.java b/src/main/java/haidnor/jvm/instruction/references/JSR_W.java deleted file mode 100644 index 09513e8..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/JSR_W.java +++ /dev/null @@ -1,26 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -/** - * 在早期版本的Java虚拟机规范中,存在JSR和JSR_W两个指令。其中,JSR指令用于进行子程序调用,而JSR_W指令用于进行宽跳转的子程序调用。宽跳转指令可以用于在字节码中的跳转偏移量超过16位时进行有效的跳转。 - *

- * 然而,从Java SE 6及以后的版本开始,Java虚拟机规范已废弃了JSR_W指令,并且不建议在新的Java代码中使用。而是通过其他方式来实现跳转,例如使用ldc_w、ldc2_w和goto_w等指令结合进行宽跳转。 - */ -public class JSR_W extends Instruction { - - public JSR_W(CodeStream codeStream) { - super(codeStream); - throw new UnsupportedOperationException("JSR_W"); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - throw new UnsupportedOperationException("JSR"); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/MONITORENTER.java b/src/main/java/haidnor/jvm/instruction/references/MONITORENTER.java deleted file mode 100644 index 58b9655..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/MONITORENTER.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -public class MONITORENTER extends Instruction { - - public MONITORENTER(CodeStream codeStream) { - super(codeStream); - throw new UnsupportedOperationException("MONITORENTER"); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - throw new UnsupportedOperationException("MONITORENTER"); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/MONITOREXIT.java b/src/main/java/haidnor/jvm/instruction/references/MONITOREXIT.java deleted file mode 100644 index 9be38f1..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/MONITOREXIT.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -public class MONITOREXIT extends Instruction { - - public MONITOREXIT(CodeStream codeStream) { - super(codeStream); - throw new UnsupportedOperationException("MONITOREXIT"); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - throw new UnsupportedOperationException("MONITOREXIT"); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/NEW.java b/src/main/java/haidnor/jvm/instruction/references/NEW.java deleted file mode 100644 index 530ed88..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/NEW.java +++ /dev/null @@ -1,44 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Instance; -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.Metaspace; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.ConstantPoolUtil; -import lombok.SneakyThrows; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.Utility; - -public class NEW extends Instruction { - - private final int constantClassIndex; - - public NEW(CodeStream codeStream) { - super(codeStream); - this.constantClassIndex = codeStream.readUnsignedShort(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ConstantPoolUtil constantPoolUtil = frame.getConstantPoolUtil(); - String className = constantPoolUtil.constantClass_ClassName(constantClassIndex); - - if (className.startsWith("java/")) { - frame.push(new StackValue(Const.T_OBJECT, null)); - return; - } - - Klass klass = Metaspace.getJavaClass(Utility.compactClassName(className)); - if (klass == null) { - // 如果在元空间中找不到已加载的类,则开始进行类加载流程 - klass = frame.getMetaClass().getClassLoader().loadClass(className); - } - Instance instance = klass.newInstance(); - frame.push(new StackValue(Const.T_OBJECT, instance)); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/NEWARRAY.java b/src/main/java/haidnor/jvm/instruction/references/NEWARRAY.java deleted file mode 100644 index 414ea28..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/NEWARRAY.java +++ /dev/null @@ -1,53 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; -import org.apache.bcel.Const; - -public class NEWARRAY extends Instruction { - - private final int type; - - public NEWARRAY(CodeStream codeStream) { - super(codeStream); - this.type = codeStream.readUnsignedByte(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - int size = frame.popInt(); - switch (type) { - case Const.T_BOOLEAN -> { - frame.pushRef(BasicTypeArray.boolArray(size)); - } - case Const.T_CHAR -> { - frame.pushRef(BasicTypeArray.charArray(size)); - } - case Const.T_FLOAT -> { - frame.pushRef(BasicTypeArray.floatArray(size)); - } - case Const.T_DOUBLE -> { - frame.pushRef(BasicTypeArray.doubleArray(size)); - } - case Const.T_BYTE -> { - frame.pushRef(BasicTypeArray.byteArray(size)); - } - case Const.T_SHORT -> { - frame.pushRef(BasicTypeArray.shortArray(size)); - } - case Const.T_INT -> { - frame.pushRef(BasicTypeArray.intArray(size)); - } - case Const.T_LONG -> { - frame.pushRef(BasicTypeArray.longArray(size)); - } - default -> throw new IllegalArgumentException(); - } - - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/PUTFIELD.java b/src/main/java/haidnor/jvm/instruction/references/PUTFIELD.java deleted file mode 100644 index 5a7f7d7..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/PUTFIELD.java +++ /dev/null @@ -1,35 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Instance; -import haidnor.jvm.rtda.KlassField; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.ConstantPoolUtil; -import lombok.SneakyThrows; - -public class PUTFIELD extends Instruction { - - private final int constantFieldrefIndex; - - public PUTFIELD(CodeStream codeStream) { - super(codeStream); - this.constantFieldrefIndex = codeStream.readUnsignedShort(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ConstantPoolUtil constantPool = frame.getConstantPoolUtil(); - String filedName = constantPool.getFieldName(constantFieldrefIndex); - String fieldSignature = constantPool.getFieldSignature(constantFieldrefIndex); - - StackValue stackValue = frame.pop(); - - Instance instance = frame.popRef(); - KlassField field = instance.getField(filedName, fieldSignature); - field.setVal(stackValue); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/references/PUTSTATIC.java b/src/main/java/haidnor/jvm/instruction/references/PUTSTATIC.java deleted file mode 100644 index 509a179..0000000 --- a/src/main/java/haidnor/jvm/instruction/references/PUTSTATIC.java +++ /dev/null @@ -1,44 +0,0 @@ -package haidnor.jvm.instruction.references; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.KlassField; -import haidnor.jvm.rtda.Metaspace; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.ConstantPoolUtil; -import lombok.SneakyThrows; -import org.apache.bcel.classfile.ConstantFieldref; -import org.apache.bcel.classfile.ConstantPool; -import org.apache.bcel.classfile.Utility; - -/** - * @author wang xiang - */ -public class PUTSTATIC extends Instruction { - - private final int constantFieldrefIndex; - - public PUTSTATIC(CodeStream codeStream) { - super(codeStream); - this.constantFieldrefIndex = codeStream.readUnsignedShort(this); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - ConstantPool constantPool = frame.getConstantPool(); - ConstantPoolUtil constantPoolUtil = frame.getConstantPoolUtil(); - ConstantFieldref constFieldref = constantPool.getConstant(constantFieldrefIndex); - // 动态链接. 找到字段所属的 Java 类 - String className = constantPoolUtil.constantFieldref_ClassName(constFieldref); - // 动态链接. 找到字段的名字 - String fieldName = constantPoolUtil.getFieldName(constFieldref); - // 以上代码体现了动态链接.Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池里指向方法的符号引用作为参数. - - Klass javaClass = Metaspace.getJavaClass(Utility.compactClassName(className)); - KlassField staticField = javaClass.getStaticField(fieldName); - staticField.setVal(frame.pop()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stack/DUP.java b/src/main/java/haidnor/jvm/instruction/stack/DUP.java deleted file mode 100644 index e29a03b..0000000 --- a/src/main/java/haidnor/jvm/instruction/stack/DUP.java +++ /dev/null @@ -1,27 +0,0 @@ -package haidnor.jvm.instruction.stack; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -public class DUP extends Instruction { - - public DUP(CodeStream codeStream) { - super(codeStream); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - StackValue stackValue = frame.pop(); - - // 判空是为了兼容创建 Java 对象, 创建rt.jar java对象时,执行 NEW 指令会在栈帧中存放一个 NULL - if (stackValue.getValue() != null) { - frame.push(stackValue); - frame.push(stackValue); - } - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stack/DUP2.java b/src/main/java/haidnor/jvm/instruction/stack/DUP2.java deleted file mode 100644 index ea7e3dd..0000000 --- a/src/main/java/haidnor/jvm/instruction/stack/DUP2.java +++ /dev/null @@ -1,41 +0,0 @@ -package haidnor.jvm.instruction.stack; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -/** - * DUP2指令是一条Java虚拟机指令,用于复制栈顶两个元素,并将复制的值插入到栈顶之后。 - *

- * DUP2指令有以下几种形式: - *

- * 栈顶元素为A的情况: - *

- * 执行DUP2指令后,栈的状态变为:A(副本)、A。 - * 栈顶元素为A和B的情况: - *

- * 执行DUP2指令后,栈的状态变为:A(副本)、B、A、B。 - * 需要注意的是,在执行DUP2指令时,操作数栈必须至少有一个或两个元素,具体取决于栈顶元素的数量。如果栈顶元素只有一个,则只会复制该元素一次;如果栈顶元素有两个,则会分别复制两个元素。 - *

- * 在Java虚拟机规范中,DUP2指令的操作码为0x5C,也属于堆栈管理指令家族(Stack Management Instructions)。 - */ -public class DUP2 extends Instruction { - - public DUP2(CodeStream codeStream) { - super(codeStream); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - StackValue stackValue1 = frame.pop(); - StackValue stackValue2 = frame.pop(); - frame.push(stackValue2); - frame.push(stackValue1); - frame.push(stackValue2); - frame.push(stackValue1); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stack/DUP2_X1.java b/src/main/java/haidnor/jvm/instruction/stack/DUP2_X1.java deleted file mode 100644 index eb94553..0000000 --- a/src/main/java/haidnor/jvm/instruction/stack/DUP2_X1.java +++ /dev/null @@ -1,36 +0,0 @@ -package haidnor.jvm.instruction.stack; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; -import org.apache.bcel.Const; - -public class DUP2_X1 extends Instruction { - - public DUP2_X1(CodeStream codeStream) { - super(codeStream); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - StackValue stackValue1 = frame.pop(); - StackValue stackValue2 = frame.pop(); - - if (stackValue1.getType() == Const.T_DOUBLE || stackValue1.getType() == Const.T_LONG) { - frame.push(stackValue1); - frame.push(stackValue2); - frame.push(stackValue1); - } else { - StackValue stackValue3 = frame.pop(); - frame.push(stackValue1); - frame.push(stackValue2); - frame.push(stackValue3); - frame.push(stackValue1); - frame.push(stackValue2); - } - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stack/DUP2_X2.java b/src/main/java/haidnor/jvm/instruction/stack/DUP2_X2.java deleted file mode 100644 index 68b8b36..0000000 --- a/src/main/java/haidnor/jvm/instruction/stack/DUP2_X2.java +++ /dev/null @@ -1,70 +0,0 @@ -package haidnor.jvm.instruction.stack; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; -import org.apache.bcel.Const; - -/** - * @author wang xiang - */ -public class DUP2_X2 extends Instruction { - - public DUP2_X2(CodeStream codeStream) { - super(codeStream); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - StackValue stackValue1 = frame.pop(); - // v1(64) - if (stackValue1.getType() == Const.T_DOUBLE || stackValue1.getType() == Const.T_LONG) { - StackValue stackValue2 = frame.pop(); - - // v1(64) v2(64) - if (stackValue2.getType() == Const.T_DOUBLE || stackValue2.getType() == Const.T_LONG) { - frame.push(stackValue1); - frame.push(stackValue2); - frame.push(stackValue1); - } - // v1(64) v2(32) - else { - StackValue stackValue3 = frame.pop(); - - frame.push(stackValue1); - frame.push(stackValue2); - frame.push(stackValue3); - frame.push(stackValue1); - } - } - // v1(32) - else { - StackValue stackValue2 = frame.pop(); - StackValue stackValue3 = frame.pop(); - - // v1(32) v2(32) v3(64) - if (stackValue3.getType() == Const.T_DOUBLE || stackValue3.getType() == Const.T_LONG) { - frame.push(stackValue1); - frame.push(stackValue2); - frame.push(stackValue3); - frame.push(stackValue1); - frame.push(stackValue2); - } - // v1(32) v2(32) v3(32) v4(32) - else { - StackValue stackValue4 = frame.pop(); - - frame.push(stackValue1); - frame.push(stackValue2); - frame.push(stackValue3); - frame.push(stackValue4); - frame.push(stackValue1); - frame.push(stackValue2); - } - } - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stack/DUP_X1.java b/src/main/java/haidnor/jvm/instruction/stack/DUP_X1.java deleted file mode 100644 index 6829c32..0000000 --- a/src/main/java/haidnor/jvm/instruction/stack/DUP_X1.java +++ /dev/null @@ -1,36 +0,0 @@ -package haidnor.jvm.instruction.stack; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -/** - * DUP_X1指令是一条Java虚拟机指令,用于复制栈顶元素并插入到栈顶下面的第二个元素位置。 - *

- * 具体而言,DUP_X1指令的作用是将栈顶元素复制一份,并将复制出的值插入到栈顶下面的第二个元素位置。这种操作会在栈上产生一个额外的副本,栈的深度会增加1。 - *

- * 例如,如果栈顶是A,下面的两个元素是B和C,执行DUP_X1指令后,栈的状态将变为:A(副本)、B、C、A。 - *

- * 在Java虚拟机规范中,DUP_X1指令的操作码为0x5A,属于堆栈管理指令家族(Stack Management Instructions)。 - *

- * 需要注意的是,执行DUP_X1指令时,操作数栈必须至少有两个元素,否则指令无法正常执行。 - */ -public class DUP_X1 extends Instruction { - - public DUP_X1(CodeStream codeStream) { - super(codeStream); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - StackValue stackValue1 = frame.pop(); - StackValue stackValue2 = frame.pop(); - frame.push(stackValue1); - frame.push(stackValue2); - frame.push(stackValue1); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stack/DUP_X2.java b/src/main/java/haidnor/jvm/instruction/stack/DUP_X2.java deleted file mode 100644 index e8430a3..0000000 --- a/src/main/java/haidnor/jvm/instruction/stack/DUP_X2.java +++ /dev/null @@ -1,47 +0,0 @@ -package haidnor.jvm.instruction.stack; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -/** - * DUP_X2指令是一条Java虚拟机指令,用于复制栈顶元素并插入到栈顶下面的第三个元素位置。 - *

- * 具体而言,DUP_X2指令的作用是将栈顶元素复制一份,并将复制出的值插入到栈顶下面的第三个元素位置。这种操作会在栈上产生一个额外的副本,栈的深度会增加1。 - *

- * 根据栈顶元素以及其后面的元素数量,DUP_X2指令有三种不同的形式: - *

- * 栈顶元素为A,下面两个元素为B和C的情况: - *

- * 执行DUP_X2指令后,栈的状态变为:A(副本)、B、C、A。 - * 栈顶元素为A,下面三个元素为B、C和D的情况: - *

- * 执行DUP_X2指令后,栈的状态变为:A(副本)、B、C、D、A。 - * 栈顶元素为A,下面一个元素为B的情况: - *

- * 执行DUP_X2指令后,栈的状态变为:A(副本)、B、A。 - * 在Java虚拟机规范中,DUP_X2指令的操作码为0x5C,也属于堆栈管理指令家族(Stack Management Instructions)。 - *

- * 需要注意的是,执行DUP_X2指令时,操作数栈必须至少有两个或三个元素,具体取决于栈顶元素和其后面的元素的数量,否则指令无法正常执行。 - */ -public class DUP_X2 extends Instruction { - - public DUP_X2(CodeStream codeStream) { - super(codeStream); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - StackValue stackValue1 = frame.pop(); - StackValue stackValue2 = frame.pop(); - StackValue stackValue3 = frame.pop(); - frame.push(stackValue1); - frame.push(stackValue3); - frame.push(stackValue2); - frame.push(stackValue1); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stack/POP.java b/src/main/java/haidnor/jvm/instruction/stack/POP.java deleted file mode 100644 index 3543f15..0000000 --- a/src/main/java/haidnor/jvm/instruction/stack/POP.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stack; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -public class POP extends Instruction { - - public POP(CodeStream codeStream) { - super(codeStream); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - frame.pop(); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stack/POP2.java b/src/main/java/haidnor/jvm/instruction/stack/POP2.java deleted file mode 100644 index 289ae75..0000000 --- a/src/main/java/haidnor/jvm/instruction/stack/POP2.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stack; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -public class POP2 extends Instruction { - - public POP2(CodeStream codeStream) { - super(codeStream); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - frame.pop(); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stack/SWAP.java b/src/main/java/haidnor/jvm/instruction/stack/SWAP.java deleted file mode 100644 index ccf515e..0000000 --- a/src/main/java/haidnor/jvm/instruction/stack/SWAP.java +++ /dev/null @@ -1,24 +0,0 @@ -package haidnor.jvm.instruction.stack; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Instance; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; -import lombok.SneakyThrows; - -public class SWAP extends Instruction { - - public SWAP(CodeStream codeStream) { - super(codeStream); - } - - @Override - @SneakyThrows - public void execute(Frame frame) { - Instance v2 = frame.popRef(); - Instance v1 = frame.popRef(); - frame.pushRef(v2); - frame.pushRef(v1); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/AASTORE.java b/src/main/java/haidnor/jvm/instruction/stores/AASTORE.java deleted file mode 100644 index 1a60aff..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/AASTORE.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.Instance; -import haidnor.jvm.rtda.InstanceArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class AASTORE extends Instruction { - - public AASTORE(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - Instance val = frame.popRef(); - int index = frame.popInt(); - InstanceArray array = (InstanceArray) frame.popRef(); - array.items[index] = val; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/ASTORE.java b/src/main/java/haidnor/jvm/instruction/stores/ASTORE.java deleted file mode 100644 index 96a1b90..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/ASTORE.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class ASTORE extends Instruction { - - private final int index; - - public ASTORE(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedByte(this); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetRef(index, value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/ASTORE_0.java b/src/main/java/haidnor/jvm/instruction/stores/ASTORE_0.java deleted file mode 100644 index 930ba45..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/ASTORE_0.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class ASTORE_0 extends Instruction { - - public ASTORE_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetRef(0, value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/ASTORE_1.java b/src/main/java/haidnor/jvm/instruction/stores/ASTORE_1.java deleted file mode 100644 index 14faefe..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/ASTORE_1.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class ASTORE_1 extends Instruction { - - public ASTORE_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetRef(1, value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/ASTORE_2.java b/src/main/java/haidnor/jvm/instruction/stores/ASTORE_2.java deleted file mode 100644 index c04efc9..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/ASTORE_2.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class ASTORE_2 extends Instruction { - - public ASTORE_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetRef(2, value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/ASTORE_3.java b/src/main/java/haidnor/jvm/instruction/stores/ASTORE_3.java deleted file mode 100644 index 966d894..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/ASTORE_3.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class ASTORE_3 extends Instruction { - - public ASTORE_3(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetRef(3, value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/BASTORE.java b/src/main/java/haidnor/jvm/instruction/stores/BASTORE.java deleted file mode 100644 index 5bf333d..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/BASTORE.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class BASTORE extends Instruction { - - public BASTORE(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int val = frame.popInt(); - int index = frame.popInt(); - BasicTypeArray array = (BasicTypeArray) frame.popRef(); - array.ints[index] = val; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/CASTORE.java b/src/main/java/haidnor/jvm/instruction/stores/CASTORE.java deleted file mode 100644 index c59dcdc..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/CASTORE.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class CASTORE extends Instruction { - - public CASTORE(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int val = frame.popInt(); - int index = frame.popInt(); - BasicTypeArray array = (BasicTypeArray) frame.popRef(); - array.ints[index] = val; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/DASTORE.java b/src/main/java/haidnor/jvm/instruction/stores/DASTORE.java deleted file mode 100644 index a4359b0..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/DASTORE.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class DASTORE extends Instruction { - - public DASTORE(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - double val = frame.popDouble(); - int index = frame.popInt(); - final BasicTypeArray array = (BasicTypeArray) frame.popRef(); - array.doubles[index] = val; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/DSTORE.java b/src/main/java/haidnor/jvm/instruction/stores/DSTORE.java deleted file mode 100644 index a6f18d9..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/DSTORE.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class DSTORE extends Instruction { - - private final int index; - - public DSTORE(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedByte(this); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetDouble(index, (double) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/DSTORE_0.java b/src/main/java/haidnor/jvm/instruction/stores/DSTORE_0.java deleted file mode 100644 index e3e188b..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/DSTORE_0.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class DSTORE_0 extends Instruction { - - public DSTORE_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetDouble(0, (double) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/DSTORE_1.java b/src/main/java/haidnor/jvm/instruction/stores/DSTORE_1.java deleted file mode 100644 index 9e9aad3..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/DSTORE_1.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class DSTORE_1 extends Instruction { - - public DSTORE_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetDouble(1, (double) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/DSTORE_2.java b/src/main/java/haidnor/jvm/instruction/stores/DSTORE_2.java deleted file mode 100644 index 943ce56..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/DSTORE_2.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class DSTORE_2 extends Instruction { - - public DSTORE_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetDouble(2, (double) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/DSTORE_3.java b/src/main/java/haidnor/jvm/instruction/stores/DSTORE_3.java deleted file mode 100644 index 874ecff..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/DSTORE_3.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class DSTORE_3 extends Instruction { - - public DSTORE_3(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetDouble(3, (double) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/FASTORE.java b/src/main/java/haidnor/jvm/instruction/stores/FASTORE.java deleted file mode 100644 index 52ee484..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/FASTORE.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class FASTORE extends Instruction { - - public FASTORE(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - float val = frame.popFloat(); - int index = frame.popInt(); - final BasicTypeArray array = (BasicTypeArray) frame.popRef(); - array.floats[index] = val; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/FSTORE.java b/src/main/java/haidnor/jvm/instruction/stores/FSTORE.java deleted file mode 100644 index 0b21d8b..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/FSTORE.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class FSTORE extends Instruction { - - private final int index; - - public FSTORE(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedByte(this); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetFloat(index, (float) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/FSTORE_0.java b/src/main/java/haidnor/jvm/instruction/stores/FSTORE_0.java deleted file mode 100644 index 9f3d662..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/FSTORE_0.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class FSTORE_0 extends Instruction { - - public FSTORE_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetFloat(0, (float) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/FSTORE_1.java b/src/main/java/haidnor/jvm/instruction/stores/FSTORE_1.java deleted file mode 100644 index 39a02d4..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/FSTORE_1.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class FSTORE_1 extends Instruction { - - public FSTORE_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetFloat(1, (float) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/FSTORE_2.java b/src/main/java/haidnor/jvm/instruction/stores/FSTORE_2.java deleted file mode 100644 index 93c878d..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/FSTORE_2.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class FSTORE_2 extends Instruction { - - public FSTORE_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetFloat(2, (float) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/FSTORE_3.java b/src/main/java/haidnor/jvm/instruction/stores/FSTORE_3.java deleted file mode 100644 index 2fbc978..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/FSTORE_3.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class FSTORE_3 extends Instruction { - - public FSTORE_3(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetFloat(3, (float) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/IASTORE.java b/src/main/java/haidnor/jvm/instruction/stores/IASTORE.java deleted file mode 100644 index ce632e4..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/IASTORE.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class IASTORE extends Instruction { - - public IASTORE(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int val = frame.popInt(); - int index = frame.popInt(); - final BasicTypeArray array = (BasicTypeArray) frame.popRef(); - array.ints[index] = val; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/ISTORE.java b/src/main/java/haidnor/jvm/instruction/stores/ISTORE.java deleted file mode 100644 index 596adcc..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/ISTORE.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class ISTORE extends Instruction { - - private final int index; - - public ISTORE(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedByte(this); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetInt(index, (int) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/ISTORE_0.java b/src/main/java/haidnor/jvm/instruction/stores/ISTORE_0.java deleted file mode 100644 index 34b1c45..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/ISTORE_0.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class ISTORE_0 extends Instruction { - - public ISTORE_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetInt(0, (int) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/ISTORE_1.java b/src/main/java/haidnor/jvm/instruction/stores/ISTORE_1.java deleted file mode 100644 index d48c064..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/ISTORE_1.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class ISTORE_1 extends Instruction { - - public ISTORE_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetInt(1, (int) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/ISTORE_2.java b/src/main/java/haidnor/jvm/instruction/stores/ISTORE_2.java deleted file mode 100644 index 166bc3a..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/ISTORE_2.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class ISTORE_2 extends Instruction { - - public ISTORE_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetInt(2, (int) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/ISTORE_3.java b/src/main/java/haidnor/jvm/instruction/stores/ISTORE_3.java deleted file mode 100644 index 7e9fe6e..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/ISTORE_3.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class ISTORE_3 extends Instruction { - - public ISTORE_3(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetInt(3, (int) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/LASTORE.java b/src/main/java/haidnor/jvm/instruction/stores/LASTORE.java deleted file mode 100644 index f8b8a7c..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/LASTORE.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class LASTORE extends Instruction { - - public LASTORE(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - long val = frame.popLong(); - int index = frame.popInt(); - final BasicTypeArray array = (BasicTypeArray) frame.popRef(); - array.longs[index] = val; - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/LSTORE.java b/src/main/java/haidnor/jvm/instruction/stores/LSTORE.java deleted file mode 100644 index 764dbfd..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/LSTORE.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class LSTORE extends Instruction { - - private final int index; - - public LSTORE(CodeStream codeStream) { - super(codeStream); - this.index = codeStream.readUnsignedByte(this); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetLong(index, (long) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/LSTORE_0.java b/src/main/java/haidnor/jvm/instruction/stores/LSTORE_0.java deleted file mode 100644 index 2c5711a..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/LSTORE_0.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class LSTORE_0 extends Instruction { - - public LSTORE_0(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetLong(0, (long) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/LSTORE_1.java b/src/main/java/haidnor/jvm/instruction/stores/LSTORE_1.java deleted file mode 100644 index 3b3d390..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/LSTORE_1.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class LSTORE_1 extends Instruction { - - public LSTORE_1(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetLong(1, (long) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/LSTORE_2.java b/src/main/java/haidnor/jvm/instruction/stores/LSTORE_2.java deleted file mode 100644 index e993b72..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/LSTORE_2.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class LSTORE_2 extends Instruction { - - public LSTORE_2(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetLong(2, (long) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/LSTORE_3.java b/src/main/java/haidnor/jvm/instruction/stores/LSTORE_3.java deleted file mode 100644 index eb4cb25..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/LSTORE_3.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.runtime.StackValue; -import haidnor.jvm.util.CodeStream; - -public class LSTORE_3 extends Instruction { - - public LSTORE_3(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - StackValue value = frame.pop(); - frame.slotSetLong(3, (long) value.getValue()); - } - -} diff --git a/src/main/java/haidnor/jvm/instruction/stores/SASTORE.java b/src/main/java/haidnor/jvm/instruction/stores/SASTORE.java deleted file mode 100644 index 4d4ebf6..0000000 --- a/src/main/java/haidnor/jvm/instruction/stores/SASTORE.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.instruction.stores; - -import haidnor.jvm.instruction.Instruction; -import haidnor.jvm.rtda.BasicTypeArray; -import haidnor.jvm.runtime.Frame; -import haidnor.jvm.util.CodeStream; - -public class SASTORE extends Instruction { - - public SASTORE(CodeStream codeStream) { - super(codeStream); - } - - @Override - public void execute(Frame frame) { - int val = frame.popInt(); - int index = frame.popInt(); - BasicTypeArray array = (BasicTypeArray) frame.popRef(); - array.ints[index] = val; - } - -} diff --git a/src/main/java/haidnor/jvm/rtda/ArrayInstance.java b/src/main/java/haidnor/jvm/rtda/ArrayInstance.java deleted file mode 100644 index c0262e9..0000000 --- a/src/main/java/haidnor/jvm/rtda/ArrayInstance.java +++ /dev/null @@ -1,15 +0,0 @@ -package haidnor.jvm.rtda; - -/** - * @author wang xiang - */ -public abstract class ArrayInstance extends Instance { - - public final int size; - - public ArrayInstance(Klass klass, int size) { - super(klass); - this.size = size; - } - -} diff --git a/src/main/java/haidnor/jvm/rtda/BasicTypeArray.java b/src/main/java/haidnor/jvm/rtda/BasicTypeArray.java deleted file mode 100644 index 4f90986..0000000 --- a/src/main/java/haidnor/jvm/rtda/BasicTypeArray.java +++ /dev/null @@ -1,65 +0,0 @@ -package haidnor.jvm.rtda; - -/** - * @author wang xiang - */ -public class BasicTypeArray extends ArrayInstance { - - public int[] ints; - public long[] longs; - public float[] floats; - public double[] doubles; - - private BasicTypeArray(int size) { - super(null, size); - } - - public static BasicTypeArray charArray(int size) { - final BasicTypeArray array = new BasicTypeArray(size); - array.ints = new int[size]; - return array; - } - - public static BasicTypeArray boolArray(int size) { - final BasicTypeArray array = new BasicTypeArray(size); - array.ints = new int[size]; - return array; - } - - public static BasicTypeArray byteArray(int size) { - final BasicTypeArray array = new BasicTypeArray(size); - array.ints = new int[size]; - return array; - } - - public static BasicTypeArray shortArray(int size) { - final BasicTypeArray array = new BasicTypeArray(size); - array.ints = new int[size]; - return array; - } - - public static BasicTypeArray intArray(int size) { - final BasicTypeArray array = new BasicTypeArray(size); - array.ints = new int[size]; - return array; - } - - public static BasicTypeArray longArray(int size) { - final BasicTypeArray array = new BasicTypeArray(size); - array.longs = new long[size]; - return array; - } - - public static BasicTypeArray floatArray(int size) { - final BasicTypeArray array = new BasicTypeArray(size); - array.floats = new float[size]; - return array; - } - - public static BasicTypeArray doubleArray(int size) { - final BasicTypeArray array = new BasicTypeArray(size); - array.doubles = new double[size]; - return array; - } - -} \ No newline at end of file diff --git a/src/main/java/haidnor/jvm/rtda/Instance.java b/src/main/java/haidnor/jvm/rtda/Instance.java deleted file mode 100644 index 37b4e2d..0000000 --- a/src/main/java/haidnor/jvm/rtda/Instance.java +++ /dev/null @@ -1,69 +0,0 @@ -package haidnor.jvm.rtda; - - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * JVM 中的 Java 对象实例 - * - * @author wang xiang - */ -public class Instance { - - public final Klass klass; - - public final List fields; - - private Instance superInstance; - - public Instance(Klass klass) { - this.klass = klass; - this.fields = new ArrayList<>(); - } - - public Instance(List fields, Klass klass) { - this.fields = fields; - this.klass = klass; - } - - - public void setSuperInstance(Instance superInstance) { - this.superInstance = superInstance; - } - - /** - * 获取字段 - * - * @param name 字段名称 - * @param signature 字段签名 - */ - public KlassField getField(String name, String signature) { - // this object - for (KlassField field : fields) { - if (Objects.equals(field.name, name) && Objects.equals(field.descriptor, signature)) { - return field; - } - } - if (this.superInstance == null) { - return null; - } - // super object - return this.superInstance.getField(name, signature); - } - -// /** -// * 设置字段 -// * -// * @param name 字段名称 -// * @param signature 字段签名 -// * @param val 值 -// */ -// public void setField(String name, String signature, UnionSlot val) { -// JavaField field = this.getField(name, signature); -// field.set(val); -// } - - -} diff --git a/src/main/java/haidnor/jvm/rtda/InstanceArray.java b/src/main/java/haidnor/jvm/rtda/InstanceArray.java deleted file mode 100644 index 64e1819..0000000 --- a/src/main/java/haidnor/jvm/rtda/InstanceArray.java +++ /dev/null @@ -1,15 +0,0 @@ -package haidnor.jvm.rtda; - -/** - * @author wang xiang - */ -public class InstanceArray extends ArrayInstance { - - public final Instance[] items; - - public InstanceArray(Klass klass, Instance[] items) { - super(klass, items.length); - this.items = items; - } - -} \ No newline at end of file diff --git a/src/main/java/haidnor/jvm/rtda/Klass.java b/src/main/java/haidnor/jvm/rtda/Klass.java deleted file mode 100644 index 1209a88..0000000 --- a/src/main/java/haidnor/jvm/rtda/Klass.java +++ /dev/null @@ -1,120 +0,0 @@ -package haidnor.jvm.rtda; - -import haidnor.jvm.classloader.ClassLoader; -import haidnor.jvm.core.JavaExecutionEngine; -import lombok.SneakyThrows; -import org.apache.bcel.classfile.Field; -import org.apache.bcel.classfile.JavaClass; -import org.apache.bcel.classfile.Method; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * 类元信息 - * - * @author wang xiang - */ -public class Klass { - - /** - * BCEL JavaClass - */ - private final JavaClass javaClass; - - /** - * 加载此 Class 的类加载器 - */ - private final ClassLoader classLoader; - - /** - * 此 Class 名称. 例如 java.util.ArrayList - */ - private final String className; - - public final String superClassName; - - private Klass superKlass; - - /** - * 静态字段 - */ - private final Map staticFieldMap = new HashMap<>(); - - /** - * 加载类元数据并将类放入元空间 - *

- * 执行顺序如下 - * 1.优先加载父类 - * 2.为类元数据创建静态字段(此时并没有给静态字符赋值) - * 3.执行 clinit 方法(静态方法), 为静态字段赋值,或执行自定义逻辑. - */ - @SneakyThrows - public Klass(ClassLoader classLoader, JavaClass javaClass) { - this.javaClass = javaClass; - this.classLoader = classLoader; - this.className = javaClass.getClassName(); - this.superClassName = javaClass.getSuperclassName(); - - // loader super class - JavaClass superJavaClass = javaClass.getSuperClass(); - if (superJavaClass != null) { - this.superKlass = new Klass(classLoader, superJavaClass); - } - - Metaspace.registerJavaClass(this); - - // init staticFieldMap - for (Field field : javaClass.getFields()) { - if (field.isStatic()) { - staticFieldMap.put(field.getName(), new KlassField(field)); - } - } - - // execute method - if (!javaClass.getClassName().startsWith("java.")) { - for (Method method : javaClass.getMethods()) { - if (method.toString().equals("static void ()")) { - KlassMethod klassMethod = new KlassMethod(this, method); - JavaExecutionEngine.callMethod(null, klassMethod); - break; - } - } - } - } - - public KlassField getStaticField(String filedName) { - return staticFieldMap.get(filedName); - } - - public Instance newInstance() { - // 创建对象存放字段的内存空间 - List klassFieldList = new ArrayList<>(); - for (Field field : javaClass.getFields()) { - KlassField klassField = new KlassField(field); - klassFieldList.add(klassField); - } - // 创建 JVM 中的对象实例 - Instance obj = new Instance(klassFieldList, this); - // 加载父类 - if (this.superKlass != null) { - obj.setSuperInstance(this.superKlass.newInstance()); - } - return obj; - } - - public JavaClass getJavaClass() { - return javaClass; - } - - public ClassLoader getClassLoader() { - return classLoader; - } - - public String getClassName() { - return className; - } - -} diff --git a/src/main/java/haidnor/jvm/rtda/KlassField.java b/src/main/java/haidnor/jvm/rtda/KlassField.java deleted file mode 100644 index 676fb37..0000000 --- a/src/main/java/haidnor/jvm/rtda/KlassField.java +++ /dev/null @@ -1,52 +0,0 @@ -package haidnor.jvm.rtda; - -import haidnor.jvm.runtime.StackValue; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.Field; - -/** - * 类元信息字段 - * - * @author wang xiang - */ -public class KlassField { - - public final int accessFlags; - - public final String name; - - public final String descriptor; - - /** - * 值类型 - */ - private int type; - /** - * 值 - */ - private Object value; - - - public KlassField(Field field) { - this.accessFlags = field.getAccessFlags(); - this.name = field.getName(); - this.descriptor = field.getSignature(); - } - - public boolean isStatic() { - return (accessFlags & Const.ACC_STATIC) != 0; - } - - public void setVal(StackValue stackValue) { - this.type = stackValue.getType(); - this.value = stackValue.getValue(); - } - - public int getType() { - return type; - } - - public Object getValue() { - return value; - } -} diff --git a/src/main/java/haidnor/jvm/rtda/KlassMethod.java b/src/main/java/haidnor/jvm/rtda/KlassMethod.java deleted file mode 100644 index 378ef44..0000000 --- a/src/main/java/haidnor/jvm/rtda/KlassMethod.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.rtda; - -import org.apache.bcel.classfile.Method; - -/** - * 类元信息方法 - * - * @author wang xiang - */ -public class KlassMethod { - - public final Klass aKlass; - - public final Method javaMethod; - - public KlassMethod(Klass klass, Method javaMethod) { - this.aKlass = klass; - this.javaMethod = javaMethod; - } - -} diff --git a/src/main/java/haidnor/jvm/rtda/Metaspace.java b/src/main/java/haidnor/jvm/rtda/Metaspace.java deleted file mode 100644 index 208c8c5..0000000 --- a/src/main/java/haidnor/jvm/rtda/Metaspace.java +++ /dev/null @@ -1,32 +0,0 @@ -package haidnor.jvm.rtda; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 元空间 - * - * @author wang xiang - */ -public class Metaspace { - - private static final Map javaClassMapMap = new ConcurrentHashMap<>(); - - /** - * 名称用.符号分割 - * 例如: haidnor.jvm.test.instruction.references.NEW - */ - public static Klass getJavaClass(String className) { - return javaClassMapMap.get(className); - } - - /** - * 注册名称用.符号分割 - * 例如: haidnor.jvm.test.instruction.references.NEW - */ - public static void registerJavaClass(Klass javaKlass) { - String className = javaKlass.getClassName(); - javaClassMapMap.put(className, javaKlass); - } - -} diff --git a/src/main/java/haidnor/jvm/rtda/package-info.java b/src/main/java/haidnor/jvm/rtda/package-info.java deleted file mode 100644 index bb8b9ce..0000000 --- a/src/main/java/haidnor/jvm/rtda/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/* -JVM(Java虚拟机)的 RTDA(Run-time Data Area)是指在程序运行时分配给Java应用程序使用的内存区域,它包含了几个重要的组成部分。 - -1.程序计数器(Program Counter Register):程序计数器是一块较小的内存区域,它保存着当前线程所执行的字节码指令的地址或索引。每个线程都有自己独立的程序计数器,用于记录线程执行的位置,以便线程切换后能够恢复执行。 - -2.Java虚拟机栈(Java Virtual Machine Stacks):每个线程在创建时都会分配一个对应的Java虚拟机栈,用于存储局部变量、方法参数、动态链接信息和方法返回值等数据。每个方法在执行时会创建一个栈帧(Stack Frame),栈帧用于存储方法的局部变量和操作数栈等信息。 - -3.本地方法栈(Native Method Stack):本地方法栈与Java虚拟机栈类似,但是它为本地方法(即用其他语言编写的方法)服务。本地方法栈也会为本地方法的调用和执行提供内存空间。 - -4.Java堆(Java Heap):Java堆是Java虚拟机管理的最大的一块内存区域,用于存储对象实例和数组数据。几乎所有的对象都在Java堆上分配内存,垃圾收集器也主要针对Java堆进行垃圾回收。 - -5.方法区(Method Area):方法区是存储类的结构信息、常量池、字段和方法的字节码等数据的内存区域。它包括运行时常量池,用于存放编译期生成的各种字面量和符号引用。 - -RTDA是JVM在运行时为Java应用程序提供的数据区域,每个线程都有自己的私有数据区域(程序计数器、虚拟机栈和本地方法栈),而Java堆和方法区则是所有线程共享的。这些区域的组合提供了Java程序运行所需的内存空间。 - -*/ -package haidnor.jvm.rtda; \ No newline at end of file diff --git a/src/main/java/haidnor/jvm/runtime/Frame.java b/src/main/java/haidnor/jvm/runtime/Frame.java deleted file mode 100644 index 5336673..0000000 --- a/src/main/java/haidnor/jvm/runtime/Frame.java +++ /dev/null @@ -1,278 +0,0 @@ -package haidnor.jvm.runtime; - -import haidnor.jvm.rtda.Instance; -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.KlassMethod; -import haidnor.jvm.util.CodeStream; -import haidnor.jvm.util.ConstantPoolUtil; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.Code; -import org.apache.bcel.classfile.ConstantPool; - -import java.util.Stack; - -/** - * JVM 线程栈帧 - * - * @author wang xiang - */ -public class Frame { - /** - * 当前栈帧所处于的 JVM 线程 - */ - private final JVMThread jvmThread; - - /** - * 栈帧所属的方法 - *

- * <<深入理解JAVA虚拟机>>: - * 每一个栈帧都包含一个指向运行时常量池中该栈帧所属的方法引用,持有这个引用的目的是为了支持方法调用过程中的动态链接(Dynamic Linking) - */ - private final org.apache.bcel.classfile.Method method; - - private final KlassMethod klassMethod; - - /** - * 每个栈帧中包含一个指向运行时常量池中该栈帧所属的方法的引用。包含这个引用的目的就是为了支持当前方法实现动态链接 - * 有了这个引用,执行引擎就可以找到指定的方法,加载字节码指令 - */ - public final Klass klass; - - /** - * 栈帧所属的方法代码对象 - */ - private final Code code; - - private final CodeStream codeStream; - - private final ConstantPool constantPool; - - private final ConstantPoolUtil constantPoolUtil; - - /** - * 操作数栈 - */ - private final Stack operandStack = new Stack<>(); - - /** - * 槽位 - */ - private final Slot[] slots; - - public Frame(JVMThread jvmThread, KlassMethod klassMethod) { - this.jvmThread = jvmThread; - this.klass = klassMethod.aKlass; - this.klassMethod = klassMethod; - this.method = klassMethod.javaMethod; - this.code = method.getCode(); - this.constantPool = method.getConstantPool(); - this.constantPoolUtil = new ConstantPoolUtil(constantPool); - this.codeStream = new CodeStream(method.getCode()); - this.slots = new Slot[code.getMaxLocals()]; - } - - public JVMThread getJvmThread() { - return jvmThread; - } - - public int getCodeLength() { - return this.code.getCode().length; - } - - public CodeStream getCodeStream() { - return codeStream; - } - - public org.apache.bcel.classfile.Method getMethod() { - return method; - } - - public ConstantPool getConstantPool() { - return constantPool; - } - - public ConstantPoolUtil getConstantPoolUtil() { - return constantPoolUtil; - } - - public KlassMethod getMetaMethod() { - return klassMethod; - } - - public Klass getMetaClass() { - return klass; - } - - /* 操作数栈操作 --------------------------------------------------------------------------------------------------- */ - - /** - * 压入操作数栈 - */ - public void push(StackValue stackValue) { - this.operandStack.push(stackValue); - } - - /** - * 弹出操作数栈顶元素 - */ - public StackValue pop() { - return this.operandStack.pop(); - } - - /** - * 弹出操作数栈顶元素 - */ - public StackValue peek() { - return this.operandStack.peek(); - } - - /** - * 从操作数栈中弹出指定数量的元素的值 - */ - public Object[] popStacksValue(int num) { - Object[] objArr = new Object[num]; - for (int i = num - 1; i >= 0; i--) { - StackValue stackValue = operandStack.pop(); - switch (stackValue.getType()) { - case Const.T_CHAR, Const.T_INT, Const.T_OBJECT, Const.T_LONG, Const.T_DOUBLE, Const.T_FLOAT: - break; - case Const.T_ARRAY: - throw new Error("数组类型,未作处理"); - default: - throw new Error("无法识别的参数类型"); - } - objArr[i] = stackValue.getValue(); - } - return objArr; - } - - - public int popInt() { - StackValue stackValue = pop(); - return (int) stackValue.getValue(); - } - - public void pushInt(int value) { - push(new StackValue(Const.T_INT, value)); - } - - public long popLong() { - StackValue stackValue = pop(); - return (long) stackValue.getValue(); - } - - public void pushLong(long value) { - push(new StackValue(Const.T_LONG, value)); - } - - public float popFloat() { - StackValue stackValue = pop(); - return (float) stackValue.getValue(); - } - - public void pushFloat(float value) { - push(new StackValue(Const.T_FLOAT, value)); - } - - public double popDouble() { - StackValue stackValue = pop(); - return (double) stackValue.getValue(); - } - - public void pushDouble(double value) { - push(new StackValue(Const.T_DOUBLE, value)); - } - - public Instance popRef() { - StackValue stackValue = pop(); - return (Instance) stackValue.getValue(); - } - - public void pushRef(Object value) { - push(new StackValue(Const.T_OBJECT, value)); - } - - /** - * 获取操作数栈中元素的数量 - */ - public int operandStackSize() { - return this.operandStack.size(); - } - - /* 局部变量表操作 --------------------------------------------------------------------------------------------------- */ - - public void slotSetInt(int index, int val) { - slots[index] = new Slot(val); - } - - public int slotGetInt(int index) { - return slots[index].num; - } - - public void slotSetFloat(int index, float val) { - int tmp = Float.floatToIntBits(val); - slots[index] = new Slot(tmp); - } - - public float slotGetFloat(int index) { - int num = slots[index].num; - return Float.intBitsToFloat(num); - } - - public long slotGetLong(int index) { - int high = slots[index].num; - int low = slots[index + 1].num; - - long l1 = (high & 0x000000ffffffffL) << 32; - long l2 = low & 0x00000000ffffffffL; - return l1 | l2; - } - - public void slotSetLong(int index, long val) { - // high 32 - int high = (int) (val >> 32); - // low 32 - int low = (int) (val & 0x000000ffffffffL); - - slots[index] = new Slot(high); - slots[index + 1] = new Slot(low); - } - - public void slotSetDouble(int index, double val) { - long tmp = Double.doubleToLongBits(val); - // high 32 - int high = (int) (tmp >> 32); - // low 32 - int low = (int) (tmp & 0x000000ffffffffL); - - slots[index] = new Slot(high); - slots[index + 1] = new Slot(low); - } - - public double slotGetDouble(int index) { - long tmp = this.slotGetLong(index); - return Double.longBitsToDouble(tmp); - } - - public void slotSetRef(int index, Object ref) { - slots[index] = new Slot(ref); - } - - public Object slotGetRef(int index) { - return slots[index].ref; - } - - public void slotSet(int index, StackValue stackValue) { - switch (stackValue.getType()) { - case Const.T_CHAR -> throw new Error("T_CHAR,未作处理"); - case Const.T_INT -> slotSetInt(index, (int) stackValue.getValue()); - case Const.T_OBJECT -> slotSetRef(index, stackValue.getValue()); - case Const.T_LONG -> slotSetLong(index, (long) stackValue.getValue()); - case Const.T_DOUBLE -> slotSetDouble(index, (double) stackValue.getValue()); - case Const.T_FLOAT -> slotSetFloat(index, (float) stackValue.getValue()); - case Const.T_ARRAY -> throw new Error("T_ARRAY,未作处理"); - default -> throw new Error("无法识别的参数类型"); - } - } - -} diff --git a/src/main/java/haidnor/jvm/runtime/JVMThread.java b/src/main/java/haidnor/jvm/runtime/JVMThread.java deleted file mode 100644 index 217b7eb..0000000 --- a/src/main/java/haidnor/jvm/runtime/JVMThread.java +++ /dev/null @@ -1,38 +0,0 @@ -package haidnor.jvm.runtime; - -import java.util.Stack; - -/** - * JVM 线程 - *

- * 目前这个 JVMThread 并没有真的被 start() 开启, 不继承 Thread 也可以 - * - * @author wang xiang - */ -public class JVMThread extends Thread { - - /** - * JVM 线程栈 - */ - private final Stack stack = new Stack<>(); - - public void push(Frame frame) { - this.stack.push(frame); - } - - public Frame peek() { - return this.stack.peek(); - } - - public void pop() { - this.stack.pop(); - } - - /** - * 获取 JVM 线程栈的栈帧数量 - */ - public int stackSize() { - return this.stack.size(); - } - -} diff --git a/src/main/java/haidnor/jvm/runtime/Slot.java b/src/main/java/haidnor/jvm/runtime/Slot.java deleted file mode 100644 index ae535d6..0000000 --- a/src/main/java/haidnor/jvm/runtime/Slot.java +++ /dev/null @@ -1,24 +0,0 @@ -package haidnor.jvm.runtime; - -/** - * @author wang xiang - */ -public class Slot { - public Integer num; - public Object ref; - - public Slot(int num) { - this.num = num; - this.ref = null; - } - - public Slot(Object ref) { - this.num = null; - this.ref = ref; - } - - @Override - public String toString() { - return "Slot{" + "num=" + num + ", ref=" + ref + '}'; - } -} diff --git a/src/main/java/haidnor/jvm/runtime/StackValue.java b/src/main/java/haidnor/jvm/runtime/StackValue.java deleted file mode 100644 index 27406b5..0000000 --- a/src/main/java/haidnor/jvm/runtime/StackValue.java +++ /dev/null @@ -1,26 +0,0 @@ -package haidnor.jvm.runtime; - -import lombok.Data; - -/** - * 操作数栈中的值对象 - * - * @author wang xiang - */ -@Data -public class StackValue { - /** - * 类型 - */ - private int type; - /** - * 值 - */ - private Object value; - - public StackValue(int type, Object val) { - this.type = type; - this.value = val; - } - -} diff --git a/src/main/java/haidnor/jvm/util/CodeStream.java b/src/main/java/haidnor/jvm/util/CodeStream.java deleted file mode 100644 index cbb927f..0000000 --- a/src/main/java/haidnor/jvm/util/CodeStream.java +++ /dev/null @@ -1,103 +0,0 @@ -package haidnor.jvm.util; - -import haidnor.jvm.instruction.Instruction; -import lombok.SneakyThrows; -import org.apache.bcel.classfile.Code; - -import java.io.DataInputStream; - -/** - * @author wang xiang - */ -public class CodeStream { - - /** - * 当前读取到的数组下标 - * -1 代表还没有开始读 - */ - private int index = -1; - - public Code code; - - private final DataInputStream codeStream; - - public CodeStream(Code code) { - this.code = code; - this.codeStream = IoUtil.getDataInputStream(code.getCode()); - } - - @SneakyThrows - public int available() { - return codeStream.available(); - } - - /** - * 读取占用一个字节指定代码 - */ - @SneakyThrows - public int readJavaVmOpcode() { - this.index += 1; - return this.codeStream.readUnsignedByte(); - } - - /** - * 读取占用一个字节的操作数 - */ - @SneakyThrows - public int readByte(Instruction instruction) { - instruction.setOffSet(instruction.offSet() + 1); - this.index += 1; - return this.codeStream.readByte(); - } - - /** - * 读取占用一个字节的操作数 - * int 类型 - */ - @SneakyThrows - public int readUnsignedByte(Instruction instruction) { - instruction.setOffSet(instruction.offSet() + 1); - this.index += 1; - return this.codeStream.readUnsignedByte(); - } - - /** - * 读取占用两个字节的操作数 - */ - @SneakyThrows - public int readShort(Instruction instruction) { - instruction.setOffSet(instruction.offSet() + 2); - this.index += 2; - return this.codeStream.readShort(); - } - - /** - * 读取占用两个字节的操作数 - * int 类型 - */ - @SneakyThrows - public int readUnsignedShort(Instruction instruction) { - instruction.setOffSet(instruction.offSet() + 2); - this.index += 2; - return this.codeStream.readUnsignedShort(); - } - - /** - * 读取占用四个字节的操作数 - */ - @SneakyThrows - public int readInt(Instruction instruction) { - instruction.setOffSet(instruction.offSet() + 4); - this.index += 4; - return this.codeStream.readInt(); - } - - public int index() { - return this.index; - } - - public Code getCode() { - return code; - } - -} diff --git a/src/main/java/haidnor/jvm/util/ConstantPoolUtil.java b/src/main/java/haidnor/jvm/util/ConstantPoolUtil.java deleted file mode 100644 index d4c5b2e..0000000 --- a/src/main/java/haidnor/jvm/util/ConstantPoolUtil.java +++ /dev/null @@ -1,180 +0,0 @@ -package haidnor.jvm.util; - -import org.apache.bcel.classfile.*; - -/** - * @author wang xiang - */ -public class ConstantPoolUtil { - - private final ConstantPool cp; - - public ConstantPoolUtil(ConstantPool cp) { - this.cp = cp; - } - - public ConstantFieldref getConstantFieldref(int constantFieldrefIndex) { - return cp.getConstant(constantFieldrefIndex); - } - - public ConstantMethodref getConstantMethodref(int constantMethodrefIndex) { - return cp.getConstant(constantMethodrefIndex); - } - - public ConstantClass getConstantClass(int constantClassIndex) { - return cp.getConstant(constantClassIndex); - } - - public ConstantNameAndType getConstantNameAndType(int constantNameAndTypeIndex) { - return cp.getConstant(constantNameAndTypeIndex); - } - - - // - - public ConstantPool getCp() { - return cp; - } - - - // ConstantClass --------------------------------------------------------------------------------------------------- - - /** - * 获取长类名, 例如 java/lang/String - */ - public String constantClass_ClassName(final ConstantClass constantClass) { - ConstantUtf8 constantUtf8 = cp.getConstant(constantClass.getNameIndex()); - return constantUtf8.getBytes(); - } - - /** - * 获取长类名, 例如 java/lang/String - */ - public String constantClass_ClassName(int constantClassIndex) { - ConstantClass constantClass = cp.getConstant(constantClassIndex); - return constantClass_ClassName(constantClass); - } - - - // ConstantFieldref ------------------------------------------------------------------------------------------------ - - /** - * 获取字段所处于Java类的类名, 例如 java/lang/String - */ - public String constantFieldref_ClassName(final ConstantFieldref constantFieldref) { - ConstantClass constClass = cp.getConstant(constantFieldref.getClassIndex()); - return (String) constClass.getConstantValue(cp); - } - - /** - * 获取字段所处于Java类的类名, 例如 java/lang/String - */ - public String constantFieldref_ClassName(int constantFieldrefIndex) { - ConstantFieldref constantFieldref = getConstantFieldref(constantFieldrefIndex); - return constantFieldref_ClassName(constantFieldref); - } - - /** - * 获取字段名称 - */ - public String getFieldName(final ConstantFieldref constantFieldref) { - ConstantNameAndType constNameAndType = cp.getConstant(constantFieldref.getNameAndTypeIndex()); - return constNameAndType.getName(cp); - } - - /** - * 获取字段名称 - */ - public String getFieldName(int constantFieldrefIndex) { - ConstantFieldref constantFieldref = getConstantFieldref(constantFieldrefIndex); - return getFieldName(constantFieldref); - } - - /** - * 获取字段类型签名 - */ - public String getFieldSignature(final ConstantFieldref constantFieldref) { - ConstantNameAndType constNameAndType = cp.getConstant(constantFieldref.getNameAndTypeIndex()); - return constNameAndType.getSignature(cp); - } - - /** - * 获取字段类型签名 - */ - public String getFieldSignature(int constantFieldrefIndex) { - ConstantFieldref constantFieldref = getConstantFieldref(constantFieldrefIndex); - return getFieldSignature(constantFieldref); - } - - // ConstantMethodref ----------------------------------------------------------------------------------------------- - - /** - * 获取方法所处于Java类的类名 - * 名称使用/分割,例如 haidnor/jvm/test/instruction/references/NEW - */ - public String constantMethodref_ClassName(final ConstantMethodref methodref) { - ConstantClass constClass = cp.getConstant(methodref.getClassIndex()); - return (String) constClass.getConstantValue(cp); - } - - /** - * 获取方法名 - */ - public String constantMethodref_MethodName(final ConstantMethodref methodref) { - ConstantNameAndType constNameAndType = cp.getConstant(methodref.getNameAndTypeIndex()); - return constNameAndType.getName(cp); - } - - /** - * 获取方法签名 - */ - public String constantMethodref_MethodSignature(final ConstantMethodref methodref) { - ConstantNameAndType constNameAndType = cp.getConstant(methodref.getNameAndTypeIndex()); - return constNameAndType.getSignature(cp); - } - - // ConstantInterfaceMethodref ----------------------------------------------------------------------------------------------- - - public String constantInterfaceMethodref_ClassName(final ConstantInterfaceMethodref methodref) { - ConstantClass constClass = cp.getConstant(methodref.getClassIndex()); - return (String) constClass.getConstantValue(cp); - } - - /** - * 获取方法名 - */ - public String constantInterfaceMethodref_MethodName(final ConstantInterfaceMethodref methodref) { - ConstantNameAndType constNameAndType = cp.getConstant(methodref.getNameAndTypeIndex()); - return constNameAndType.getName(cp); - } - - /** - * 获取方法签名 - */ - public String constantInterfaceMethodref_MethodSignature(final ConstantInterfaceMethodref methodref) { - ConstantNameAndType constNameAndType = cp.getConstant(methodref.getNameAndTypeIndex()); - return constNameAndType.getSignature(cp); - } - - - // ConstantNameAndType ----------------------------------------------------------------------------------------------- - - /** - * ConstantNameAndType - */ - public ConstantNameAndType constantNameAndType(int constantNameAndTypeIndex) { - return cp.getConstant(constantNameAndTypeIndex); - } - - public String constantNameAndType_name(int constantNameAndTypeIndex) { - ConstantNameAndType constantNameAndType = constantNameAndType(constantNameAndTypeIndex); - return constantNameAndType.getName(cp); - } - - public String constantNameAndType_signature(int constantNameAndTypeIndex) { - ConstantNameAndType constantNameAndType = constantNameAndType(constantNameAndTypeIndex); - return constantNameAndType.getSignature(cp); - } - - -} diff --git a/src/main/java/haidnor/jvm/util/IoUtil.java b/src/main/java/haidnor/jvm/util/IoUtil.java deleted file mode 100644 index bd59036..0000000 --- a/src/main/java/haidnor/jvm/util/IoUtil.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.util; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; - -/** - * @author wang xiang - */ -public abstract class IoUtil { - - public static DataInputStream getDataInputStream(byte[] bytes) { - ByteArrayInputStream inputStream = getByteArrayInputStream(bytes); - return new DataInputStream(inputStream); - } - - public static ByteArrayInputStream getByteArrayInputStream(byte[] bytes) { - return new ByteArrayInputStream(bytes); - } - -} diff --git a/src/main/java/haidnor/jvm/util/JavaClassUtil.java b/src/main/java/haidnor/jvm/util/JavaClassUtil.java deleted file mode 100644 index 2a09354..0000000 --- a/src/main/java/haidnor/jvm/util/JavaClassUtil.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.util; - -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.KlassMethod; -import org.apache.bcel.classfile.JavaClass; - -/** - * @author wang xiang - */ -public abstract class JavaClassUtil { - - /** - * 获取 main 方法 - */ - public static KlassMethod getMainMethod(Klass aKlass) { - JavaClass javaClass = aKlass.getJavaClass(); - for (org.apache.bcel.classfile.Method method : javaClass.getMethods()) { - if (method.toString().startsWith("public static void main(String[] args)")) { - return new KlassMethod(aKlass, method); - } - } - return null; - } - -} diff --git a/src/main/java/haidnor/jvm/util/JvmThreadHolder.java b/src/main/java/haidnor/jvm/util/JvmThreadHolder.java deleted file mode 100644 index 0b9350a..0000000 --- a/src/main/java/haidnor/jvm/util/JvmThreadHolder.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.util; - -import haidnor.jvm.runtime.JVMThread; - -/** - * @author wang xiang - */ -public abstract class JvmThreadHolder { - - private static final ThreadLocal holder = new ThreadLocal<>(); - - public static void set(JVMThread thread) { - holder.set(thread); - } - - public static JVMThread get() { - return holder.get(); - } - -} diff --git a/src/main/java/haidnor/jvm/util/SignatureUtil.java b/src/main/java/haidnor/jvm/util/SignatureUtil.java deleted file mode 100644 index 9f647bd..0000000 --- a/src/main/java/haidnor/jvm/util/SignatureUtil.java +++ /dev/null @@ -1,37 +0,0 @@ -package haidnor.jvm.util; - -import lombok.SneakyThrows; -import org.apache.bcel.classfile.Utility; - -/** - * @author wang xiang - */ -public abstract class SignatureUtil { - - /** - * 解析方法签名返回方法参数类型数组 - */ - @SneakyThrows - public static Class[] getParameterTypes(String methodeSignature) { - String[] argumentTypeArr = Utility.methodSignatureArgumentTypes(methodeSignature, false); - Class[] argumentClassArr = new Class[argumentTypeArr.length]; - for (int i = 0; i < argumentTypeArr.length; i++) { - Class argumentClass; - String argumentType = argumentTypeArr[i]; - argumentClass = switch (argumentType) { - case "byte" -> byte.class; - case "short" -> short.class; - case "boolean" -> boolean.class; - case "char" -> char.class; - case "int" -> int.class; - case "long" -> long.class; - case "float" -> float.class; - case "double" -> double.class; - default -> Class.forName(argumentType); - }; - argumentClassArr[i] = argumentClass; - } - return argumentClassArr; - } - -} diff --git a/src/test/java/haidnor/jvm/test/BCELTest.java b/src/test/java/haidnor/jvm/test/BCELTest.java new file mode 100644 index 0000000..36a90c2 --- /dev/null +++ b/src/test/java/haidnor/jvm/test/BCELTest.java @@ -0,0 +1,15 @@ +package haidnor.jvm.test; + +import haidnor.jvm.bcel.classfile.ClassParser; +import haidnor.jvm.bcel.classfile.JavaClass; + +import java.io.FileInputStream; +import java.io.IOException; + +public class BCELTest { + public static void main(String[] args) throws IOException { + ClassParser classParser = new ClassParser(new FileInputStream("D:\\project_javaXL\\xuanleOa\\project-main\\target\\classes\\com\\xuanleOa\\XuanleOaApplication.class"), null); + JavaClass javaClass = classParser.parse(); + System.out.println(javaClass); + } +} diff --git a/src/test/java/haidnor/jvm/test/TestJVM.java b/src/test/java/haidnor/jvm/test/TestJVM.java deleted file mode 100644 index 1066c64..0000000 --- a/src/test/java/haidnor/jvm/test/TestJVM.java +++ /dev/null @@ -1,190 +0,0 @@ -package haidnor.jvm.test; - -import haidnor.jvm.classloader.ClassLoader; -import haidnor.jvm.core.JavaExecutionEngine; -import haidnor.jvm.rtda.Klass; -import haidnor.jvm.rtda.KlassMethod; -import haidnor.jvm.rtda.Metaspace; -import haidnor.jvm.runtime.JVMThread; -import haidnor.jvm.test.demo.*; -import haidnor.jvm.test.instruction.Array; -import haidnor.jvm.test.instruction.DO_WHILE; -import haidnor.jvm.test.instruction.math.ISUB; -import haidnor.jvm.test.instruction.math.LSUB; -import haidnor.jvm.test.instruction.references.NEW; -import haidnor.jvm.util.JavaClassUtil; -import haidnor.jvm.util.JvmThreadHolder; -import org.junit.Test; - -import java.io.IOException; - -public class TestJVM { - - public static void runMainClass(java.lang.Class mainClass) throws IOException { - JvmThreadHolder.set(new JVMThread()); - ClassLoader bootClassLoader = new ClassLoader("ApplicationClassLoader"); - Klass mainMeteKlass = bootClassLoader.loadClass(mainClass.getName().replace('.', '/')); - KlassMethod mainKlassMethod = JavaClassUtil.getMainMethod(mainMeteKlass); - Metaspace.registerJavaClass(mainMeteKlass); - JavaExecutionEngine.callMainMethod(mainKlassMethod); - } - - /** - * hello,world - */ - @Test - public void test_1() throws Exception { - runMainClass(demo_helloWorld.class); - } - - @Test - public void test_2() throws Exception { - runMainClass(Demo2.class); - } - - @Test - public void test_3() throws Exception { - runMainClass(Demo3.class); - } - - @Test - public void test_4() throws Exception { - runMainClass(Demo4.class); - } - - @Test - public void test_5() throws Exception { - runMainClass(Demo5.class); - } - - @Test - public void test_6() throws Exception { - runMainClass(Demo6.class); - } - - @Test - public void test_7() throws Exception { - runMainClass(Demo7.class); - } - - @Test - public void test_8() throws Exception { - runMainClass(Demo8.class); - } - - @Test - public void demo_while() throws Exception { - runMainClass(demo_while.class); - } - - @Test - public void demo_doWhile() throws Exception { - runMainClass(demo_doWhile.class); - } - - @Test - public void demo_for() throws Exception { - runMainClass(demo_for.class); - } - - @Test - public void demo_foreach() throws Exception { - runMainClass(demo_foreach_1.class); - } - - /** - * --add-opens java.base/java.util=ALL-UNNAMED - */ - @Test - public void demo_foreach_2() throws Exception { - // runMainClass(demo_foreach_2.class); - } - - /** - * --add-opens java.base/java.util=ALL-UNNAMED - */ - @Test - public void demo_foreach_3() throws Exception { - // TODO wangxiang fix bug - // runMainClass(demo_foreach_3.class); - } - - @Test(expected = ArithmeticException.class) - public void demo_exception_1() throws Exception { - runMainClass(demo_exception_1.class); - } - - @Test - public void demo_exception_2() throws Exception { - runMainClass(demo_exception_2.class); - } - - @Test - public void demo_exception_3() throws Exception { - runMainClass(demo_exception_3.class); - } - - @Test(expected = ArithmeticException.class) - public void demo_exception_4() throws Exception { - runMainClass(demo_exception_4.class); - } - - @Test - public void demo_finally_1() throws Exception { - runMainClass(demo_finally_1.class); - } - - @Test(expected = ArithmeticException.class) - public void demo_finally_2() throws Exception { - runMainClass(demo_finally_2.class); - } - - @Test(expected = ArithmeticException.class) - public void demo_finally_3() throws Exception { - runMainClass(demo_finally_3.class); - } - - - @Test - public void demo_enum_1() throws Exception { - // TODO support enum - // runMainClass(demo_enum_1.class); - } - - @Test - public void test_NEW() throws Exception { - runMainClass(NEW.class); - } - - @Test - public void test_ISUB() throws Exception { - runMainClass(ISUB.class); - } - - @Test - public void test_LSUB() throws Exception { - runMainClass(LSUB.class); - } - - @Test - public void test_DOWHILE() throws Exception { - runMainClass(DO_WHILE.class); - } - - @Test - public void test_Array() throws Exception { - runMainClass(Array.class); - } - - @Test - public void test_() throws Exception { - long longNum = 922337203685477580L; // Long Max - - int[] arr = new int[2]; - arr[0] = (int) (longNum >> 32); // 获取高位部分 - arr[1] = (int) (longNum & 0xFFFFFFFFL); // 获取低位部分 - - long result = ((long) arr[0] << 32) | ((long) arr[1] & 0xFFFFFFFFL); - System.out.println(result); - } -} diff --git a/src/test/java/haidnor/jvm/test/clazz/Func.java b/src/test/java/haidnor/jvm/test/clazz/Func.java deleted file mode 100644 index 96212c9..0000000 --- a/src/test/java/haidnor/jvm/test/clazz/Func.java +++ /dev/null @@ -1,7 +0,0 @@ -package haidnor.jvm.test.clazz; - -@FunctionalInterface -public interface Func { - - boolean func(String str); -} diff --git a/src/test/java/haidnor/jvm/test/clazz/Human.java b/src/test/java/haidnor/jvm/test/clazz/Human.java deleted file mode 100644 index 0bc3a54..0000000 --- a/src/test/java/haidnor/jvm/test/clazz/Human.java +++ /dev/null @@ -1,16 +0,0 @@ -package haidnor.jvm.test.clazz; - -public abstract class Human implements Organism { - - public static final String HUMAN_NAME = "123"; - - static { - System.out.println("Human 类被加载了"); - } - - public void eat() { - Object o = new Object(); - System.out.println("人类吃饭"); - } - -} diff --git a/src/test/java/haidnor/jvm/test/clazz/Organism.java b/src/test/java/haidnor/jvm/test/clazz/Organism.java deleted file mode 100644 index 3e6f9b1..0000000 --- a/src/test/java/haidnor/jvm/test/clazz/Organism.java +++ /dev/null @@ -1,8 +0,0 @@ -package haidnor.jvm.test.clazz; - -/** - * 生物 - */ -public interface Organism extends Organism0 { - -} diff --git a/src/test/java/haidnor/jvm/test/clazz/Organism0.java b/src/test/java/haidnor/jvm/test/clazz/Organism0.java deleted file mode 100644 index fd9a393..0000000 --- a/src/test/java/haidnor/jvm/test/clazz/Organism0.java +++ /dev/null @@ -1,12 +0,0 @@ -package haidnor.jvm.test.clazz; - -/** - * 生物 - */ -public interface Organism0 { - - default void die() { - System.out.println("Organism die 0"); - } - -} diff --git a/src/test/java/haidnor/jvm/test/clazz/Student.java b/src/test/java/haidnor/jvm/test/clazz/Student.java deleted file mode 100644 index f393a22..0000000 --- a/src/test/java/haidnor/jvm/test/clazz/Student.java +++ /dev/null @@ -1,19 +0,0 @@ -package haidnor.jvm.test.clazz; - -public class Student extends Human implements Organism { - - public void method1() { - System.out.println("method1"); - method2(); - } - - public void method2() { - System.out.println("method2"); - method3(); - } - - public void method3() { - System.out.println("method3"); - } - -} diff --git a/src/test/java/haidnor/jvm/test/clazz/StudentEnum.java b/src/test/java/haidnor/jvm/test/clazz/StudentEnum.java deleted file mode 100644 index 8996e49..0000000 --- a/src/test/java/haidnor/jvm/test/clazz/StudentEnum.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.test.clazz; - -public enum StudentEnum { - - ZHANG_SAN(1,"张三"); - - private int age; - private String name; - - StudentEnum(int age, String name) { - this.age = age; - this.name = name; - } - - public int getAge() { - return age; - } - - public String getName() { - return name; - } - -} diff --git a/src/test/java/haidnor/jvm/test/demo/Demo2.java b/src/test/java/haidnor/jvm/test/demo/Demo2.java deleted file mode 100644 index 77ae088..0000000 --- a/src/test/java/haidnor/jvm/test/demo/Demo2.java +++ /dev/null @@ -1,12 +0,0 @@ -package haidnor.jvm.test.demo; - -public class Demo2 { - - public static void main(String[] args) { - long longVal = 1L; - float floatVal = 1f; - double doubleVal = 1D; - int intVal = 1; - } - -} diff --git a/src/test/java/haidnor/jvm/test/demo/Demo3.java b/src/test/java/haidnor/jvm/test/demo/Demo3.java deleted file mode 100644 index 97b40ca..0000000 --- a/src/test/java/haidnor/jvm/test/demo/Demo3.java +++ /dev/null @@ -1,22 +0,0 @@ -package haidnor.jvm.test.demo; - -import haidnor.jvm.test.clazz.Student; - -import java.util.HashMap; - -public class Demo3 { - - public static void main(String[] args) { - Student sut1 = new Student(); - Student sut2 = new Student(); - - HashMap hashMap = new HashMap<>(); - hashMap.put(sut1, "张三123"); - hashMap.put(sut2, "张三"); - - System.out.println(hashMap.get(sut1)); // 1 - System.out.println(hashMap.get(sut2)); // 2 - } - -} - diff --git a/src/test/java/haidnor/jvm/test/demo/Demo4.java b/src/test/java/haidnor/jvm/test/demo/Demo4.java deleted file mode 100644 index 23532f4..0000000 --- a/src/test/java/haidnor/jvm/test/demo/Demo4.java +++ /dev/null @@ -1,12 +0,0 @@ -package haidnor.jvm.test.demo; - -import haidnor.jvm.test.clazz.Student; - -public class Demo4 { - - public static void main(String[] args) { - Student student = new Student(); - student.method1(); - } - -} diff --git a/src/test/java/haidnor/jvm/test/demo/Demo5.java b/src/test/java/haidnor/jvm/test/demo/Demo5.java deleted file mode 100644 index 2b42a16..0000000 --- a/src/test/java/haidnor/jvm/test/demo/Demo5.java +++ /dev/null @@ -1,27 +0,0 @@ -package haidnor.jvm.test.demo; - -public class Demo5 { - - static { - System.out.println("hello,world"); - } - - public static void main(String[] args) { - String str = method1("hello world"); - method1(str); - } - - public static String method1(String s) { - return method2(s); - } - - public static String method2(String s) { - return method3(s); - } - - public static String method3(String s) { - System.out.println(s); - return "你好 世界"; - } - -} diff --git a/src/test/java/haidnor/jvm/test/demo/Demo6.java b/src/test/java/haidnor/jvm/test/demo/Demo6.java deleted file mode 100644 index 751d337..0000000 --- a/src/test/java/haidnor/jvm/test/demo/Demo6.java +++ /dev/null @@ -1,16 +0,0 @@ -package haidnor.jvm.test.demo; - - -import haidnor.jvm.test.clazz.Human; -import haidnor.jvm.test.clazz.Student; - -public class Demo6 { - - public static void main(String[] args) { - Student student = new Student(); - if (student instanceof Human) { - System.out.println("true"); - } - } - -} diff --git a/src/test/java/haidnor/jvm/test/demo/Demo7.java b/src/test/java/haidnor/jvm/test/demo/Demo7.java deleted file mode 100644 index 3c12882..0000000 --- a/src/test/java/haidnor/jvm/test/demo/Demo7.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.test.demo; - - -public class Demo7 { - - public static void main(String[] args) { - try { - String name = fun(); - System.out.println(name); - } catch (Exception exception) { - System.out.println("计算错误"); - } - } - - public static String fun() { - System.out.println(1 / 0); - return "zhangsan"; - } - -} diff --git a/src/test/java/haidnor/jvm/test/demo/Demo8.java b/src/test/java/haidnor/jvm/test/demo/Demo8.java deleted file mode 100644 index 3b13d12..0000000 --- a/src/test/java/haidnor/jvm/test/demo/Demo8.java +++ /dev/null @@ -1,17 +0,0 @@ -package haidnor.jvm.test.demo; - - -import haidnor.jvm.test.clazz.Human; -import haidnor.jvm.test.clazz.Organism0; -import haidnor.jvm.test.clazz.Student; - -public class Demo8 { - - public static void main(String[] args) { - Human organism1 = new Student(); - Organism0 organism = new Student(); - organism.die(); - } - - -} diff --git a/src/test/java/haidnor/jvm/test/demo/demo_doWhile.java b/src/test/java/haidnor/jvm/test/demo/demo_doWhile.java deleted file mode 100644 index 3794892..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_doWhile.java +++ /dev/null @@ -1,27 +0,0 @@ -package haidnor.jvm.test.demo; - -public class demo_doWhile { - public static void main(String[] args) { - int a = 0; - do { - a++; - } while (a < 2); - System.out.println(a); - } -} - -/* -main(String[] args): - 0 iconst_0 - 1 istore_1 - - 2 iinc 1 by 1 - 5 iload_1 - 6 iconst_2 - 7 if_icmplt 2 (-5) < while (a < 2); 符合条件则向前跳转 - - 10 getstatic #7 - 13 iload_1 - 14 invokevirtual #13 - 17 return - */ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/demo/demo_enum_1.java b/src/test/java/haidnor/jvm/test/demo/demo_enum_1.java deleted file mode 100644 index a5472c5..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_enum_1.java +++ /dev/null @@ -1,11 +0,0 @@ -package haidnor.jvm.test.demo; - -import haidnor.jvm.test.clazz.StudentEnum; - -public class demo_enum_1 { - public static void main(String[] args) { - StudentEnum studentEnum = StudentEnum.ZHANG_SAN; - System.out.println(studentEnum.getAge()); - System.out.println(studentEnum.getName()); - } -} diff --git a/src/test/java/haidnor/jvm/test/demo/demo_exception_1.java b/src/test/java/haidnor/jvm/test/demo/demo_exception_1.java deleted file mode 100644 index 4fd0885..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_exception_1.java +++ /dev/null @@ -1,37 +0,0 @@ -package haidnor.jvm.test.demo; - -/** - * 调用方没有捕获调用方法的异常 - */ -public class demo_exception_1 { - - public static void main(String[] args) { - int a = 0; - fun(a); - } - - public static void fun(int a) { - if (a == 0) { - throw new ArithmeticException(); - } - } - -} -/* -main(String[] args) - 0 iconst_0 - 1 istore_1 - 2 iload_1 - 3 invokestatic #7 - 6 return - -fun(int a) - 0 iload_0 - 1 ifne 12 (+11) - 4 new #29 - 7 dup - 8 invokespecial #31 : ()V> - 11 athrow - 12 return - -*/ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/demo/demo_exception_2.java b/src/test/java/haidnor/jvm/test/demo/demo_exception_2.java deleted file mode 100644 index d9dc426..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_exception_2.java +++ /dev/null @@ -1,53 +0,0 @@ -package haidnor.jvm.test.demo; - -/** - * 调用方捕获调用方法的异常 - */ -public class demo_exception_2 { - - public static void main(String[] args) { - int a = 0; - try { - fun(a); - } catch (Exception exception) { - System.out.println("catch Exception"); - } - } - - public static void fun(int a) { - if (a == 0) { - throw new ArithmeticException(); - } - } - -} -/* -main(String[] args) - 0 iconst_0 - 1 istore_1 - 2 iload_1 - 3 invokestatic #7 - 6 goto 18 (+12) - 9 astore_2 - 10 getstatic #15 - 13 ldc #21 - 15 invokevirtual #23 - 18 return - -+--------+-------+--------+--------+---------------------+ -| Nr. | 起始PC | 结束PC | 跳转PC | 捕获类型 | -+--------+-------+--------+--------+---------------------+ -| 0 | 2 | 6 | 9 | java/lang/Exception | -+--------+-------+--------+--------+---------------------+ - - -fun(int a) - 0 iload_0 - 1 ifne 12 (+11) - 4 new #29 - 7 dup - 8 invokespecial #31 : ()V> - 11 athrow - 12 return - -*/ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/demo/demo_exception_3.java b/src/test/java/haidnor/jvm/test/demo/demo_exception_3.java deleted file mode 100644 index 57f9893..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_exception_3.java +++ /dev/null @@ -1,50 +0,0 @@ -package haidnor.jvm.test.demo; - -/** - * 调用方捕获调用方法的异常的父类 - */ -public class demo_exception_3 { - - public static void main(String[] args) { - int a = 0; - try { - fun(a); - } catch (Exception exception) { - System.out.println("catch Exception"); - } - } - - public static void fun(int a) { - System.out.println(1 / a); - } - -} -/* -main(String[] args) - 0 iconst_0 - 1 istore_1 - 2 iload_1 - 3 invokestatic #7 - 6 goto 18 (+12) - 9 astore_2 - 10 getstatic #15 - 13 ldc #21 - 15 invokevirtual #23 - 18 return - -+--------+-------+--------+--------+---------------------+ -| Nr. | 起始PC | 结束PC | 跳转PC | 捕获类型 | -+--------+-------+--------+--------+---------------------+ -| 0 | 2 | 6 | 9 | java/lang/Exception | -+--------+-------+--------+--------+---------------------+ - - -fun(int a) - 0 getstatic #15 - 3 iconst_1 - 4 iload_0 - 5 idiv - 6 invokevirtual #29 - 9 return - -*/ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/demo/demo_exception_4.java b/src/test/java/haidnor/jvm/test/demo/demo_exception_4.java deleted file mode 100644 index c22e908..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_exception_4.java +++ /dev/null @@ -1,50 +0,0 @@ -package haidnor.jvm.test.demo; - -/** - * 调用方没有捕获调用方法抛出的异常类型 - */ -public class demo_exception_4 { - - public static void main(String[] args) { - int a = 0; - try { - fun(a); - } catch (NullPointerException exception) { - System.out.println("catch Exception"); - } - } - - public static void fun(int a) { - System.out.println(1 / a); - } - -} -/* -main(String[] args) - 0 iconst_0 - 1 istore_1 - 2 iload_1 - 3 invokestatic #7 - 6 goto 18 (+12) - 9 astore_2 - 10 getstatic #15 - 13 ldc #21 - 15 invokevirtual #23 - 18 return - -+--------+-------+--------+--------+--------------------------------+ -| Nr. | 起始PC | 结束PC | 跳转PC | 捕获类型 | -+--------+-------+--------+--------+--------------------------------+ -| 0 | 2 | 6 | 9 | java/lang/NullPointerException | -+--------+-------+--------+--------+--------------------------------+ - - -fun(int a) - 0 getstatic #15 - 3 iconst_1 - 4 iload_0 - 5 idiv - 6 invokevirtual #29 - 9 return - -*/ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/demo/demo_finally_1.java b/src/test/java/haidnor/jvm/test/demo/demo_finally_1.java deleted file mode 100644 index 23398c3..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_finally_1.java +++ /dev/null @@ -1,51 +0,0 @@ -package haidnor.jvm.test.demo; - -public class demo_finally_1 { - - public static void main(String[] args) { - String name = fun(); - System.out.println(name); - } - - public static String fun() { - String str = "zhang san"; - try { - return str; - } finally { - str = "li si"; - } - } - -} -/* -main(String[] args): - 0 invokestatic #7 - 3 astore_1 - 4 getstatic #13 - 7 aload_1 - 8 invokevirtual #19 - 11 return - -fun(): - 0 ldc #25 - 2 astore_0 - 3 aload_0 - 4 astore_1 - 5 ldc #27

  • - 7 astore_0 - 8 aload_1 - 9 areturn - 10 astore_2 < 如果 3-5 指令出现了异常时才会跳转到 10. 如果出现了异常会把异常压入操作数栈,这里的 astore_2 就是把异常先存储起来。 - 11 ldc #27
  • - 13 astore_0 - 14 aload_2 < 执行完 finally 把 astore_2 的异常压入操作树栈 - 15 athrow < 把操作数栈上的异常抛出 - -Exception Table: -+--------+-------+--------+--------+---------+ -| Nr. | 起始PC | 结束PC | 跳转PC | 捕获类型 | -+--------+-------+--------+--------+---------+ -| 0 | 3 | 5 | 10 | any | -+--------+-------+--------+--------+---------+ - -*/ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/demo/demo_finally_2.java b/src/test/java/haidnor/jvm/test/demo/demo_finally_2.java deleted file mode 100644 index 4474c5e..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_finally_2.java +++ /dev/null @@ -1,55 +0,0 @@ -package haidnor.jvm.test.demo; - -/** - * try 异常后执行 finally - */ -public class demo_finally_2 { - - public static void main(String[] args) { - fun(); - } - - public static void fun() { - try { - System.out.println(1 / 0); - } finally { - System.out.println("此时应该执行 finally"); - } - } - -} -/* -main(String[] args): - 0 invokestatic #7 - 3 return - - -fun(): - 0 getstatic #12 - 3 iconst_1 - 4 iconst_0 - 5 idiv - 6 invokevirtual #18 - 9 getstatic #12 - 12 ldc #24 - 14 invokevirtual #26 - 17 getstatic #12 - 20 ldc #29 <此时不应该执行 finally> - 22 invokevirtual #26 - 25 goto 39 (+14) - 28 astore_0 - 29 getstatic #12 - 32 ldc #29 <此时不应该执行 finally> - 34 invokevirtual #26 - 37 aload_0 - 38 athrow - 39 return - -Exception Table: -+--------+-------+--------+--------+---------+ -| Nr. | 起始PC | 结束PC | 跳转PC | 捕获类型 | -+--------+-------+--------+--------+---------+ -| 0 | 9 | 17 | 28 | any | -+--------+-------+--------+--------+---------+ - -*/ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/demo/demo_finally_3.java b/src/test/java/haidnor/jvm/test/demo/demo_finally_3.java deleted file mode 100644 index f854020..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_finally_3.java +++ /dev/null @@ -1,55 +0,0 @@ -package haidnor.jvm.test.demo; - -/** - * try 异常前不应该执行 finally - */ -public class demo_finally_3 { - - public static void main(String[] args) { - fun(); - } - - public static void fun() { - System.out.println(1 / 0); - try { - System.out.println("try"); - } finally { - System.out.println("finally"); - } - } - -} -/* -main(String[] args): - 0 invokestatic #7 - 3 return - -fun(): - 0 getstatic #12 - 3 iconst_1 - 4 iconst_0 - 5 idiv - 6 invokevirtual #18 - 9 getstatic #12 - 12 ldc #24 - 14 invokevirtual #26 - 17 getstatic #12 - 20 ldc #29 - 22 invokevirtual #26 - 25 goto 39 (+14) - 28 astore_0 - 29 getstatic #12 - 32 ldc #29 - 34 invokevirtual #26 - 37 aload_0 - 38 athrow - 39 return - -Exception Table: -+--------+-------+--------+--------+---------+ -| Nr. | 起始PC | 结束PC | 跳转PC | 捕获类型 | -+--------+-------+--------+--------+---------+ -| 0 | 9 | 17 | 28 | any | -+--------+-------+--------+--------+---------+ - -*/ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/demo/demo_for.java b/src/test/java/haidnor/jvm/test/demo/demo_for.java deleted file mode 100644 index 4f93b21..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_for.java +++ /dev/null @@ -1,31 +0,0 @@ -package haidnor.jvm.test.demo; - -public class demo_for { - public static void main(String[] args) { - int a = 0; - for (int i = 0; i < 3; i++) { - a++; - } - System.out.println(a); - } -} - -/* -main(String[] args): - 0 iconst_0 < int a = 0 - 1 istore_1 - 2 iconst_0 < int i =0 - 3 istore_2 - - 4 iload_2 - 5 iconst_3 < 比较值3 - 6 if_icmpge 18 (+12) < (i < 3) - 9 iinc 1 by 1 < a++ - 12 iinc 2 by 1 < i++ - 15 goto 4 (-11) < 向前跳转代码 - - 18 getstatic #7 - 21 iload_1 - 22 invokevirtual #13 - 25 return - */ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/demo/demo_foreach_1.java b/src/test/java/haidnor/jvm/test/demo/demo_foreach_1.java deleted file mode 100644 index e92f727..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_foreach_1.java +++ /dev/null @@ -1,62 +0,0 @@ -package haidnor.jvm.test.demo; - -public class demo_foreach_1 { - public static void main(String[] args) { - int[] arr = new int[]{999, 333}; - for (int i : arr) { - System.out.println(i); - } - } -} - -/* -main(String[] args): - 0 iconst_2 < 操作数栈压入2,这个是 arr[] 的长度 - 1 newarray 10 (int) < 创建数组 - 3 dup - - 4 iconst_0 - 5 sipush 999 - 8 iastore < arr[0] = 999 - - 9 dup < 复制栈顶的 arr[], 压入操作数栈 - - 10 iconst_1 - 11 sipush 333 - 14 iastore < arr[1] = 333 - - 15 astore_1 - 16 aload_1 - 17 astore_2 - 18 aload_2 - 19 arraylength - 20 istore_3 - 21 iconst_0 - 22 istore 4 - - 24 iload 4 - 26 iload_3 - 27 if_icmpge 50 (+23) - 30 aload_2 - 31 iload 4 - 33 iaload - 34 istore 5 - 36 getstatic #7 - 39 iload 5 - 41 invokevirtual #13 - 44 iinc 4 by 1 - 47 goto 24 (-23) - - 50 return - -+--------+-------+--------+--------+-----------+-------------------------+ -| Nr. | 起始PC | 长度 | 序号 | 名字 | 描述符(存储类型) | -+--------+-------+--------+--------+-----------+-------------------------+ -| 0 | 0 | 51 | 0 | args | [Ljava/lang/String; | -+--------+-------+--------+--------+-----------+-------------------------+ -| 1 | 16 | 35 | 1 | arr | [I | -+--------+-------+--------+--------+-----------+-------------------------+ -| 2 | 36 | 8 | 5 | i | I | -+--------+-------+--------+--------+-----------+-------------------------+ - - */ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/demo/demo_foreach_2.java b/src/test/java/haidnor/jvm/test/demo/demo_foreach_2.java deleted file mode 100644 index 7c34984..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_foreach_2.java +++ /dev/null @@ -1,25 +0,0 @@ -package haidnor.jvm.test.demo; - -import java.util.ArrayList; -import java.util.List; - -/** - * 需要添加启动参数 - * --add-opens java.base/java.util=ALL-UNNAMED - */ -public class demo_foreach_2 { - - public static void main(String[] args) { - List list = new ArrayList<>(); - list.add(1); - list.add(2); - for (int i = 0; i < 1000; i++) { - list.add(i); - } - - for (Integer integer : list) { - System.out.println(integer); - } - } - -} diff --git a/src/test/java/haidnor/jvm/test/demo/demo_foreach_3.java b/src/test/java/haidnor/jvm/test/demo/demo_foreach_3.java deleted file mode 100644 index 95b5488..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_foreach_3.java +++ /dev/null @@ -1,20 +0,0 @@ -package haidnor.jvm.test.demo; - -import java.util.List; - -/** - * 需要添加启动参数 - * --add-opens java.base/java.util=ALL-UNNAMED - */ -public class demo_foreach_3 { - - public static void main(String[] args) { - List list = List.of(1, 2, 3, 4, 5); - list.add(6); - -// for (Integer integer : list) { -// System.out.println(integer); -// } - } - -} diff --git a/src/test/java/haidnor/jvm/test/demo/demo_helloWorld.java b/src/test/java/haidnor/jvm/test/demo/demo_helloWorld.java deleted file mode 100644 index 5d1b057..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_helloWorld.java +++ /dev/null @@ -1,18 +0,0 @@ -package haidnor.jvm.test.demo; - -public class demo_helloWorld { - - public static void main(String[] args) { - System.out.println("hello,world"); - } - -} -/* - -main(String[] args) - 0 getstatic #7 - 3 ldc #13 - 5 invokevirtual #15 - 8 return - - */ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/demo/demo_while.java b/src/test/java/haidnor/jvm/test/demo/demo_while.java deleted file mode 100644 index bb08e63..0000000 --- a/src/test/java/haidnor/jvm/test/demo/demo_while.java +++ /dev/null @@ -1,29 +0,0 @@ -package haidnor.jvm.test.demo; - -public class demo_while { - public static void main(String[] args) { - int a = 0; - while (a < 2) { - a++; - } - System.out.println(a); - } -} - -/* -main(String[] args): - 0 iconst_0 - 1 istore_1 - 2 iload_1 - 3 iconst_2 - - 4 if_icmpge 13 (+9) < 符合条件就进入循环体 - 7 iinc 1 by 1 < a++ - 10 goto 2 (-8) < 向前跳转指令 - - 13 getstatic #7 - 16 iload_1 - 17 invokevirtual #13 - 20 return - - */ \ No newline at end of file diff --git a/src/test/java/haidnor/jvm/test/instruction/Array.java b/src/test/java/haidnor/jvm/test/instruction/Array.java deleted file mode 100644 index efece48..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/Array.java +++ /dev/null @@ -1,10 +0,0 @@ -package haidnor.jvm.test.instruction; - -import haidnor.jvm.test.instruction.loads.Student1; - -public class Array { - public static void main(String[] args) { - Student1[] strArr = new Student1[10]; - strArr[5] = new Student1(); - } -} diff --git a/src/test/java/haidnor/jvm/test/instruction/ClassA.java b/src/test/java/haidnor/jvm/test/instruction/ClassA.java deleted file mode 100644 index 17dcc7c..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/ClassA.java +++ /dev/null @@ -1,12 +0,0 @@ -package haidnor.jvm.test.instruction; - -public class ClassA { - public static class ClassB { - - public int a; - - public ClassB(int a) { - this.a = a; - } - } -} diff --git a/src/test/java/haidnor/jvm/test/instruction/DO_WHILE.java b/src/test/java/haidnor/jvm/test/instruction/DO_WHILE.java deleted file mode 100644 index 3427855..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/DO_WHILE.java +++ /dev/null @@ -1,11 +0,0 @@ -package haidnor.jvm.test.instruction; - -public class DO_WHILE { - public static void main(String[] args) { - int a = 3; - do { - System.out.println(a); - a--; - } while (a != 0); - } -} diff --git a/src/test/java/haidnor/jvm/test/instruction/GOTO.java b/src/test/java/haidnor/jvm/test/instruction/GOTO.java deleted file mode 100644 index bbe4134..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/GOTO.java +++ /dev/null @@ -1,13 +0,0 @@ -package haidnor.jvm.test.instruction; - -public class GOTO { - - public static void main(String[] args) { - int a = 1; - while (a < 10) { - System.out.println(a); - a++; - } - } - -} diff --git a/src/test/java/haidnor/jvm/test/instruction/InnerClass.java b/src/test/java/haidnor/jvm/test/instruction/InnerClass.java deleted file mode 100644 index c407f98..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/InnerClass.java +++ /dev/null @@ -1,9 +0,0 @@ -package haidnor.jvm.test.instruction; - -public class InnerClass { - public static void main(String[] args) { - ClassA.ClassB classB = new ClassA.ClassB(999); - - System.out.println(classB.a); - } -} diff --git a/src/test/java/haidnor/jvm/test/instruction/loads/Person.java b/src/test/java/haidnor/jvm/test/instruction/loads/Person.java deleted file mode 100644 index 012d01b..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/loads/Person.java +++ /dev/null @@ -1,4 +0,0 @@ -package haidnor.jvm.test.instruction.loads; - -public abstract class Person { -} diff --git a/src/test/java/haidnor/jvm/test/instruction/loads/Student.java b/src/test/java/haidnor/jvm/test/instruction/loads/Student.java deleted file mode 100644 index 2ae059c..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/loads/Student.java +++ /dev/null @@ -1,31 +0,0 @@ -package haidnor.jvm.test.instruction.loads; - -public class Student { - - public byte byteVal; - - public short shortVal; - - public float floatVal; - - public double doubleVal; - - public int intVal; - - public long longVal; - - public char charVal; - - public boolean booleanVal; - - public Student(byte byteVal, short shortVal, float floatVal, double doubleVal, int intVal, long longVal, char charVal, boolean booleanVal) { - this.byteVal = byteVal; - this.shortVal = shortVal; - this.floatVal = floatVal; - this.doubleVal = doubleVal; - this.intVal = intVal; - this.longVal = longVal; - this.charVal = charVal; - this.booleanVal = booleanVal; - } -} diff --git a/src/test/java/haidnor/jvm/test/instruction/loads/Student1.java b/src/test/java/haidnor/jvm/test/instruction/loads/Student1.java deleted file mode 100644 index 3a552a8..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/loads/Student1.java +++ /dev/null @@ -1,13 +0,0 @@ -package haidnor.jvm.test.instruction.loads; - -public class Student1 { - - public int a() { - return b(); - } - - public int b() { - return 233; - } - -} diff --git a/src/test/java/haidnor/jvm/test/instruction/math/DADD.java b/src/test/java/haidnor/jvm/test/instruction/math/DADD.java deleted file mode 100644 index 34b8e7b..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/math/DADD.java +++ /dev/null @@ -1,15 +0,0 @@ -package haidnor.jvm.test.instruction.math; - -public class DADD { - - public static void main(String[] args) { - double c = add(1D, 2D); - System.out.println(c); - } - - public static double add(double a, double b) { - double c = a + b; - return c; - } - -} diff --git a/src/test/java/haidnor/jvm/test/instruction/math/FADD.java b/src/test/java/haidnor/jvm/test/instruction/math/FADD.java deleted file mode 100644 index 94bf9fa..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/math/FADD.java +++ /dev/null @@ -1,15 +0,0 @@ -package haidnor.jvm.test.instruction.math; - -public class FADD { - - public static void main(String[] args) { - float c = add(1, 2); - System.out.println(c); - } - - public static float add(float a, float b) { - float c = a + b; - return c; - } - -} diff --git a/src/test/java/haidnor/jvm/test/instruction/math/IADD.java b/src/test/java/haidnor/jvm/test/instruction/math/IADD.java deleted file mode 100644 index e033ba7..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/math/IADD.java +++ /dev/null @@ -1,15 +0,0 @@ -package haidnor.jvm.test.instruction.math; - -public class IADD { - - public static void main(String[] args) { - int c = add(1, 2); - System.out.println(c); - } - - public static int add(int a, int b) { - int c = a + b; - return c; - } - -} diff --git a/src/test/java/haidnor/jvm/test/instruction/math/IINC.java b/src/test/java/haidnor/jvm/test/instruction/math/IINC.java deleted file mode 100644 index 4396311..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/math/IINC.java +++ /dev/null @@ -1,23 +0,0 @@ -package haidnor.jvm.test.instruction.math; - -public class IINC { - - public static void main(String[] args) { - int a = -127; - a++; - System.out.println(a); - - int b = 129; - b++; - System.out.println(b); - - int c = 321; - c++; - System.out.println(c); - - int d = 0; - d++; - System.out.println(d); - } - -} diff --git a/src/test/java/haidnor/jvm/test/instruction/math/ISUB.java b/src/test/java/haidnor/jvm/test/instruction/math/ISUB.java deleted file mode 100644 index ebdc7a0..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/math/ISUB.java +++ /dev/null @@ -1,10 +0,0 @@ -package haidnor.jvm.test.instruction.math; - -public class ISUB { - public static void main(String[] args) { - int a = 2; - int b = 1; - int c = a - b; - System.out.println(c); - } -} diff --git a/src/test/java/haidnor/jvm/test/instruction/math/LADD.java b/src/test/java/haidnor/jvm/test/instruction/math/LADD.java deleted file mode 100644 index dd3abb2..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/math/LADD.java +++ /dev/null @@ -1,15 +0,0 @@ -package haidnor.jvm.test.instruction.math; - -public class LADD { - - public static void main(String[] args) { - long c = add(1, 2); - System.out.println(c); - } - - public static long add(long a, long b) { - long c = a + b; - return c; - } - -} diff --git a/src/test/java/haidnor/jvm/test/instruction/math/LSUB.java b/src/test/java/haidnor/jvm/test/instruction/math/LSUB.java deleted file mode 100644 index dd65c37..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/math/LSUB.java +++ /dev/null @@ -1,10 +0,0 @@ -package haidnor.jvm.test.instruction.math; - -public class LSUB { - public static void main(String[] args) { - long a = 2000L; - long b = 1000L; - long c = a - b; - System.out.println(c); - } -} diff --git a/src/test/java/haidnor/jvm/test/instruction/references/CallStaticMethod.java b/src/test/java/haidnor/jvm/test/instruction/references/CallStaticMethod.java deleted file mode 100644 index f32ff38..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/references/CallStaticMethod.java +++ /dev/null @@ -1,14 +0,0 @@ -package haidnor.jvm.test.instruction.references; - -public class CallStaticMethod { - - public static void main(String[] args) { - float i = staticMethod(); - System.out.println(i); - } - - public static float staticMethod() { - return 1321.321f; - } - -} diff --git a/src/test/java/haidnor/jvm/test/instruction/references/NEW.java b/src/test/java/haidnor/jvm/test/instruction/references/NEW.java deleted file mode 100644 index 746a5fb..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/references/NEW.java +++ /dev/null @@ -1,21 +0,0 @@ -package haidnor.jvm.test.instruction.references; - -import java.io.IOException; - -public class NEW { - - public static void main(String[] args) throws IOException { - add(1, 2, 3, 4, 5, 6, 7); - } - - public static void add(int a, int b, int c, int d, int e, int f, int g) { - System.out.println(a); - System.out.println(b); - System.out.println(c); - System.out.println(d); - System.out.println(e); - System.out.println(f); - System.out.println(g); - } - -}