aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2021-10-07 23:50:25 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2021-10-07 23:50:25 +0000
commitb55a854bf7718855f681e4f57ddcff0280bc4fd7 (patch)
tree9d896c2e475840c036965d3978f05e6d234e2126
parentf7c4b954072e563b75f6910c25bb689bbf38a3d1 (diff)
parent527b85820701fdfeada1b01a4d364eb79ab4852b (diff)
downloadjavassist-b55a854bf7718855f681e4f57ddcff0280bc4fd7.tar.gz
Merge "Merge Android 12"
-rw-r--r--.gitignore15
-rw-r--r--Android.bp43
-rw-r--r--Android.mk27
-rw-r--r--HOWTO.txt4
-rw-r--r--LICENSE202
-rw-r--r--License.html27
-rw-r--r--METADATA18
-rw-r--r--MODULE_LICENSE_APACHE2 (renamed from MODULE_LICENSE_MPL)0
-rw-r--r--NOTICE25
-rw-r--r--README.md40
-rw-r--r--Readme.html149
-rw-r--r--build.properties4
-rw-r--r--build.xml73
-rw-r--r--pom.xml174
-rw-r--r--regenerate_from_source.sh14
-rw-r--r--sample/preproc/Assistant.java5
-rw-r--r--sample/preproc/Compiler.java5
-rw-r--r--src/main/META-INF/MANIFEST.MF9
-rw-r--r--src/main/javassist/ByteArrayClassPath.java56
-rw-r--r--src/main/javassist/CannotCompileException.java14
-rw-r--r--src/main/javassist/ClassClassPath.java38
-rw-r--r--src/main/javassist/ClassMap.java46
-rw-r--r--src/main/javassist/ClassPath.java14
-rw-r--r--src/main/javassist/ClassPool.java329
-rw-r--r--src/main/javassist/ClassPoolTail.java130
-rw-r--r--src/main/javassist/CodeConverter.java115
-rw-r--r--src/main/javassist/CtArray.java50
-rw-r--r--src/main/javassist/CtBehavior.java189
-rw-r--r--src/main/javassist/CtClass.java342
-rw-r--r--src/main/javassist/CtClassType.java529
-rw-r--r--src/main/javassist/CtConstructor.java25
-rw-r--r--src/main/javassist/CtField.java228
-rw-r--r--src/main/javassist/CtMember.java72
-rw-r--r--src/main/javassist/CtMethod.java40
-rw-r--r--src/main/javassist/CtNewClass.java12
-rw-r--r--src/main/javassist/CtNewConstructor.java38
-rw-r--r--src/main/javassist/CtNewMethod.java57
-rw-r--r--src/main/javassist/CtNewNestedClass.java66
-rw-r--r--src/main/javassist/CtNewWrappedConstructor.java10
-rw-r--r--src/main/javassist/CtNewWrappedMethod.java27
-rw-r--r--src/main/javassist/CtPrimitiveType.java7
-rw-r--r--src/main/javassist/Loader.java162
-rw-r--r--src/main/javassist/LoaderClassPath.java46
-rw-r--r--src/main/javassist/Modifier.java43
-rw-r--r--src/main/javassist/NotFoundException.java8
-rw-r--r--src/main/javassist/SerialVersionUID.java45
-rw-r--r--src/main/javassist/Translator.java9
-rw-r--r--src/main/javassist/URLClassPath.java27
-rw-r--r--src/main/javassist/bytecode/AccessFlag.java13
-rw-r--r--src/main/javassist/bytecode/AnnotationDefaultAttribute.java29
-rw-r--r--src/main/javassist/bytecode/AnnotationsAttribute.java151
-rw-r--r--src/main/javassist/bytecode/AttributeInfo.java152
-rw-r--r--src/main/javassist/bytecode/BadBytecode.java14
-rw-r--r--src/main/javassist/bytecode/BootstrapMethodsAttribute.java124
-rw-r--r--src/main/javassist/bytecode/ByteArray.java5
-rw-r--r--src/main/javassist/bytecode/ByteStream.java10
-rw-r--r--src/main/javassist/bytecode/Bytecode.java115
-rw-r--r--src/main/javassist/bytecode/ClassFile.java291
-rw-r--r--src/main/javassist/bytecode/ClassFilePrinter.java29
-rw-r--r--src/main/javassist/bytecode/ClassFileWriter.java73
-rw-r--r--src/main/javassist/bytecode/CodeAnalyzer.java9
-rw-r--r--src/main/javassist/bytecode/CodeAttribute.java59
-rw-r--r--src/main/javassist/bytecode/CodeIterator.java178
-rw-r--r--src/main/javassist/bytecode/ConstPool.java1653
-rw-r--r--src/main/javassist/bytecode/ConstantAttribute.java10
-rw-r--r--src/main/javassist/bytecode/DeprecatedAttribute.java8
-rw-r--r--src/main/javassist/bytecode/Descriptor.java134
-rw-r--r--src/main/javassist/bytecode/DuplicateMemberException.java8
-rw-r--r--src/main/javassist/bytecode/EnclosingMethodAttribute.java19
-rw-r--r--src/main/javassist/bytecode/ExceptionTable.java58
-rw-r--r--src/main/javassist/bytecode/ExceptionsAttribute.java12
-rw-r--r--src/main/javassist/bytecode/FieldInfo.java52
-rw-r--r--src/main/javassist/bytecode/InnerClassesAttribute.java71
-rw-r--r--src/main/javassist/bytecode/InstructionPrinter.java26
-rw-r--r--src/main/javassist/bytecode/LineNumberAttribute.java8
-rw-r--r--src/main/javassist/bytecode/LocalVariableAttribute.java15
-rw-r--r--src/main/javassist/bytecode/LocalVariableTypeAttribute.java10
-rw-r--r--src/main/javassist/bytecode/LongVector.java5
-rw-r--r--src/main/javassist/bytecode/MethodInfo.java75
-rw-r--r--src/main/javassist/bytecode/MethodParametersAttribute.java88
-rw-r--r--src/main/javassist/bytecode/Mnemonic.java10
-rw-r--r--src/main/javassist/bytecode/NestHostAttribute.java67
-rw-r--r--src/main/javassist/bytecode/NestMembersAttribute.java88
-rw-r--r--src/main/javassist/bytecode/Opcode.java8
-rw-r--r--src/main/javassist/bytecode/ParameterAnnotationsAttribute.java51
-rw-r--r--src/main/javassist/bytecode/SignatureAttribute.java459
-rw-r--r--src/main/javassist/bytecode/SourceFileAttribute.java8
-rw-r--r--src/main/javassist/bytecode/StackMap.java65
-rw-r--r--src/main/javassist/bytecode/StackMapTable.java182
-rw-r--r--src/main/javassist/bytecode/SyntheticAttribute.java8
-rw-r--r--src/main/javassist/bytecode/TypeAnnotationsAttribute.java381
-rw-r--r--src/main/javassist/bytecode/analysis/Analyzer.java20
-rw-r--r--src/main/javassist/bytecode/analysis/ControlFlow.java519
-rw-r--r--src/main/javassist/bytecode/analysis/Executor.java26
-rw-r--r--src/main/javassist/bytecode/analysis/Frame.java6
-rw-r--r--src/main/javassist/bytecode/analysis/FramePrinter.java5
-rw-r--r--src/main/javassist/bytecode/analysis/IntQueue.java7
-rw-r--r--src/main/javassist/bytecode/analysis/MultiArrayType.java21
-rw-r--r--src/main/javassist/bytecode/analysis/MultiType.java100
-rw-r--r--src/main/javassist/bytecode/analysis/Subroutine.java24
-rw-r--r--src/main/javassist/bytecode/analysis/SubroutineScanner.java17
-rw-r--r--src/main/javassist/bytecode/analysis/Type.java76
-rw-r--r--src/main/javassist/bytecode/analysis/Util.java5
-rw-r--r--src/main/javassist/bytecode/analysis/package.html5
-rw-r--r--src/main/javassist/bytecode/annotation/Annotation.java101
-rw-r--r--src/main/javassist/bytecode/annotation/AnnotationImpl.java52
-rw-r--r--src/main/javassist/bytecode/annotation/AnnotationMemberValue.java18
-rw-r--r--src/main/javassist/bytecode/annotation/AnnotationsWriter.java78
-rw-r--r--src/main/javassist/bytecode/annotation/ArrayMemberValue.java19
-rw-r--r--src/main/javassist/bytecode/annotation/BooleanMemberValue.java17
-rw-r--r--src/main/javassist/bytecode/annotation/ByteMemberValue.java17
-rw-r--r--src/main/javassist/bytecode/annotation/CharMemberValue.java17
-rw-r--r--src/main/javassist/bytecode/annotation/ClassMemberValue.java25
-rw-r--r--src/main/javassist/bytecode/annotation/DoubleMemberValue.java17
-rw-r--r--src/main/javassist/bytecode/annotation/EnumMemberValue.java12
-rw-r--r--src/main/javassist/bytecode/annotation/FloatMemberValue.java17
-rw-r--r--src/main/javassist/bytecode/annotation/IntegerMemberValue.java17
-rw-r--r--src/main/javassist/bytecode/annotation/LongMemberValue.java17
-rw-r--r--src/main/javassist/bytecode/annotation/MemberValue.java18
-rw-r--r--src/main/javassist/bytecode/annotation/MemberValueVisitor.java3
-rw-r--r--src/main/javassist/bytecode/annotation/NoSuchClassError.java7
-rw-r--r--src/main/javassist/bytecode/annotation/ShortMemberValue.java17
-rw-r--r--src/main/javassist/bytecode/annotation/StringMemberValue.java15
-rw-r--r--src/main/javassist/bytecode/annotation/TypeAnnotationsWriter.java175
-rw-r--r--src/main/javassist/bytecode/package.html4
-rw-r--r--src/main/javassist/bytecode/stackmap/BasicBlock.java134
-rw-r--r--src/main/javassist/bytecode/stackmap/Liveness.java365
-rw-r--r--src/main/javassist/bytecode/stackmap/MapMaker.java370
-rw-r--r--src/main/javassist/bytecode/stackmap/Tracer.java172
-rw-r--r--src/main/javassist/bytecode/stackmap/TypeData.java1107
-rw-r--r--src/main/javassist/bytecode/stackmap/TypeTag.java16
-rw-r--r--src/main/javassist/bytecode/stackmap/TypedBlock.java48
-rw-r--r--src/main/javassist/compiler/AccessorMaker.java28
-rw-r--r--src/main/javassist/compiler/CodeGen.java247
-rw-r--r--src/main/javassist/compiler/CompileError.java9
-rw-r--r--src/main/javassist/compiler/Javac.java102
-rw-r--r--src/main/javassist/compiler/JvstCodeGen.java113
-rw-r--r--src/main/javassist/compiler/JvstTypeChecker.java27
-rw-r--r--src/main/javassist/compiler/KeywordTable.java20
-rw-r--r--src/main/javassist/compiler/Lex.java160
-rw-r--r--src/main/javassist/compiler/MemberCodeGen.java208
-rw-r--r--src/main/javassist/compiler/MemberResolver.java205
-rw-r--r--src/main/javassist/compiler/NoFieldException.java7
-rw-r--r--src/main/javassist/compiler/Parser.java157
-rw-r--r--src/main/javassist/compiler/ProceedHandler.java6
-rw-r--r--src/main/javassist/compiler/SymbolTable.java15
-rw-r--r--src/main/javassist/compiler/SyntaxError.java8
-rw-r--r--src/main/javassist/compiler/TokenId.java5
-rw-r--r--src/main/javassist/compiler/TypeChecker.java158
-rw-r--r--src/main/javassist/compiler/ast/ASTList.java25
-rw-r--r--src/main/javassist/compiler/ast/ASTree.java10
-rw-r--r--src/main/javassist/compiler/ast/ArrayInit.java10
-rw-r--r--src/main/javassist/compiler/ast/AssignExpr.java9
-rw-r--r--src/main/javassist/compiler/ast/BinExpr.java9
-rw-r--r--src/main/javassist/compiler/ast/CallExpr.java10
-rw-r--r--src/main/javassist/compiler/ast/CastExpr.java11
-rw-r--r--src/main/javassist/compiler/ast/CondExpr.java10
-rw-r--r--src/main/javassist/compiler/ast/Declarator.java14
-rw-r--r--src/main/javassist/compiler/ast/DoubleConst.java11
-rw-r--r--src/main/javassist/compiler/ast/Expr.java11
-rw-r--r--src/main/javassist/compiler/ast/FieldDecl.java11
-rw-r--r--src/main/javassist/compiler/ast/InstanceOfExpr.java10
-rw-r--r--src/main/javassist/compiler/ast/IntConst.java11
-rw-r--r--src/main/javassist/compiler/ast/Keyword.java9
-rw-r--r--src/main/javassist/compiler/ast/Member.java10
-rw-r--r--src/main/javassist/compiler/ast/MethodDecl.java8
-rw-r--r--src/main/javassist/compiler/ast/NewExpr.java14
-rw-r--r--src/main/javassist/compiler/ast/Pair.java13
-rw-r--r--src/main/javassist/compiler/ast/Stmnt.java14
-rw-r--r--src/main/javassist/compiler/ast/StringL.java9
-rw-r--r--src/main/javassist/compiler/ast/Symbol.java9
-rw-r--r--src/main/javassist/compiler/ast/Variable.java9
-rw-r--r--src/main/javassist/compiler/ast/Visitor.java7
-rw-r--r--src/main/javassist/convert/TransformAccessArrayField.java11
-rw-r--r--src/main/javassist/convert/TransformAfter.java9
-rw-r--r--src/main/javassist/convert/TransformBefore.java15
-rw-r--r--src/main/javassist/convert/TransformCall.java14
-rw-r--r--src/main/javassist/convert/TransformFieldAccess.java11
-rw-r--r--src/main/javassist/convert/TransformNew.java16
-rw-r--r--src/main/javassist/convert/TransformNewClass.java13
-rw-r--r--src/main/javassist/convert/TransformReadField.java12
-rw-r--r--src/main/javassist/convert/TransformWriteField.java11
-rw-r--r--src/main/javassist/convert/Transformer.java5
-rw-r--r--src/main/javassist/expr/Cast.java35
-rw-r--r--src/main/javassist/expr/ConstructorCall.java8
-rw-r--r--src/main/javassist/expr/Expr.java48
-rw-r--r--src/main/javassist/expr/ExprEditor.java29
-rw-r--r--src/main/javassist/expr/FieldAccess.java41
-rw-r--r--src/main/javassist/expr/Handler.java35
-rw-r--r--src/main/javassist/expr/Instanceof.java33
-rw-r--r--src/main/javassist/expr/MethodCall.java33
-rw-r--r--src/main/javassist/expr/NewArray.java42
-rw-r--r--src/main/javassist/expr/NewExpr.java49
-rw-r--r--src/main/javassist/package.html4
-rw-r--r--src/main/javassist/runtime/Cflow.java20
-rw-r--r--src/main/javassist/runtime/Desc.java33
-rw-r--r--src/main/javassist/runtime/DotClass.java5
-rw-r--r--src/main/javassist/runtime/Inner.java5
-rw-r--r--src/main/javassist/scopedpool/ScopedClassPool.java32
-rw-r--r--src/main/javassist/scopedpool/ScopedClassPoolFactory.java5
-rw-r--r--src/main/javassist/scopedpool/ScopedClassPoolFactoryImpl.java5
-rw-r--r--src/main/javassist/scopedpool/ScopedClassPoolRepository.java7
-rw-r--r--src/main/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java52
-rw-r--r--src/main/javassist/scopedpool/SoftValueHashMap.java143
-rw-r--r--src/main/javassist/tools/Callback.java154
-rw-r--r--src/main/javassist/tools/Dump.java12
-rw-r--r--src/main/javassist/tools/framedump.java9
-rw-r--r--src/main/javassist/tools/reflect/CannotCreateException.java8
-rw-r--r--src/main/javassist/tools/reflect/CannotInvokeException.java8
-rw-r--r--src/main/javassist/tools/reflect/CannotReflectException.java9
-rw-r--r--src/main/javassist/tools/reflect/ClassMetaobject.java46
-rw-r--r--src/main/javassist/tools/reflect/Compiler.java14
-rw-r--r--src/main/javassist/tools/reflect/Loader.java29
-rw-r--r--src/main/javassist/tools/reflect/Metalevel.java5
-rw-r--r--src/main/javassist/tools/reflect/Metaobject.java29
-rw-r--r--src/main/javassist/tools/reflect/Reflection.java47
-rw-r--r--src/main/javassist/tools/reflect/Sample.java11
-rw-r--r--src/main/javassist/tools/rmi/AppletServer.java52
-rw-r--r--src/main/javassist/tools/rmi/ObjectImporter.java39
-rw-r--r--src/main/javassist/tools/rmi/ObjectNotFoundException.java8
-rw-r--r--src/main/javassist/tools/rmi/Proxy.java5
-rw-r--r--src/main/javassist/tools/rmi/RemoteException.java8
-rw-r--r--src/main/javassist/tools/rmi/RemoteRef.java7
-rw-r--r--src/main/javassist/tools/rmi/Sample.java5
-rw-r--r--src/main/javassist/tools/rmi/StubGenerator.java51
-rw-r--r--src/main/javassist/tools/web/BadHttpRequest.java11
-rw-r--r--src/main/javassist/tools/web/Viewer.java29
-rw-r--r--src/main/javassist/tools/web/Webserver.java31
-rw-r--r--src/main/javassist/util/HotSwapAgent.java223
-rw-r--r--src/main/javassist/util/HotSwapper.java88
-rw-r--r--src/main/javassist/util/proxy/DefineClassHelper.java342
-rw-r--r--src/main/javassist/util/proxy/DefinePackageHelper.java182
-rw-r--r--src/main/javassist/util/proxy/FactoryHelper.java110
-rw-r--r--src/main/javassist/util/proxy/MethodFilter.java5
-rw-r--r--src/main/javassist/util/proxy/MethodHandler.java9
-rw-r--r--src/main/javassist/util/proxy/Proxy.java33
-rw-r--r--src/main/javassist/util/proxy/ProxyFactory.java686
-rw-r--r--src/main/javassist/util/proxy/ProxyObject.java19
-rw-r--r--src/main/javassist/util/proxy/ProxyObjectInputStream.java17
-rw-r--r--src/main/javassist/util/proxy/ProxyObjectOutputStream.java16
-rw-r--r--src/main/javassist/util/proxy/RuntimeSupport.java114
-rwxr-xr-x[-rw-r--r--]src/main/javassist/util/proxy/SecurityActions.java237
-rw-r--r--src/main/javassist/util/proxy/SerializedProxy.java43
-rw-r--r--src/test/DefineClassCapability.java5
-rw-r--r--src/test/Jassist150.java111
-rw-r--r--src/test/Readme.txt18
-rw-r--r--src/test/Test.java55
-rw-r--r--src/test/VisibleTop.java7
-rw-r--r--src/test/VisibleTop2.java7
-rw-r--r--src/test/annotation/Test.java35
-rw-r--r--src/test/javassist/Bench.java162
-rw-r--r--src/test/javassist/ClassPoolBench.java58
-rw-r--r--src/test/javassist/HotswapTest.java38
-rw-r--r--src/test/javassist/JvstTest.java1185
-rw-r--r--src/test/javassist/JvstTest2.java1530
-rw-r--r--src/test/javassist/JvstTest3.java1127
-rw-r--r--src/test/javassist/JvstTest4.java1119
-rw-r--r--src/test/javassist/JvstTest5.java456
-rw-r--r--src/test/javassist/JvstTestRoot.java53
-rw-r--r--src/test/javassist/LoaderTestByRandall.java99
-rw-r--r--src/test/javassist/SetterTest.java114
-rw-r--r--src/test/javassist/bytecode/BytecodeTest.java835
-rw-r--r--src/test/javassist/bytecode/CodeAnalyzerTest.java48
-rw-r--r--src/test/javassist/bytecode/Gap0Example.classbin0 -> 3558 bytes
-rw-r--r--src/test/javassist/bytecode/InsertGap0.java231
-rw-r--r--src/test/javassist/bytecode/StackMapTest.java833
-rw-r--r--src/test/javassist/compiler/CodeTest.java26
-rw-r--r--src/test/javassist/compiler/CompTest.java150
-rw-r--r--src/test/javassist/compiler/LexTest.java21
-rw-r--r--src/test/javassist/compiler/ParseTest.java13
-rw-r--r--src/test/javassist/proxyfactory/MyCls.java29
-rw-r--r--src/test/javassist/proxyfactory/ProxyFactoryTest.java155
-rw-r--r--src/test/javassist/proxyfactory/Tester.java58
-rw-r--r--src/test/javassist/tools/CallbackTest.java41
-rw-r--r--src/test/javassist/tools/reflect/ClassMetaobjectTest.java14
-rw-r--r--src/test/javassist/tools/reflect/LoaderTest.java73
-rw-r--r--src/test/javassist/tools/reflect/Person.java64
-rw-r--r--src/test/javassist/tools/reflect/SubClass.java20
-rw-r--r--src/test/javassist/tools/reflect/SuperClass.java7
-rw-r--r--src/test/resources/Readme.txt16
-rw-r--r--src/test/resources/empty.jarbin0 -> 3360 bytes
-rw-r--r--src/test/resources/simple.jarbin0 -> 631 bytes
-rw-r--r--src/test/scoped/ScopedRepositoryTestCase.java242
-rw-r--r--src/test/scoped/TestAnnotation.java34
-rw-r--r--src/test/scoped/UnscopedAnnotationDefaultUsage.java29
-rw-r--r--src/test/scoped/UnscopedAnnotationUsage.java29
-rw-r--r--src/test/scoped/jar1/FullyScopedAnnotationDefaultUsage.java29
-rw-r--r--src/test/scoped/jar1/FullyScopedAnnotationUsage.java29
-rw-r--r--src/test/scoped/jar1/ScopedAnnotationDefaultUsage.java31
-rw-r--r--src/test/scoped/jar1/ScopedAnnotationUsage.java31
-rw-r--r--src/test/scoped/jar1/ScopedTestAnnotation.java34
-rw-r--r--src/test/scoped/jar1/TestClass1.java26
-rw-r--r--src/test/test/javassist/DefineClassCapability.java7
-rw-r--r--src/test/test/javassist/bytecode/analysis/AnalyzerTest.java17
-rw-r--r--src/test/test/javassist/bytecode/analysis/DomTreePrinter.java77
-rw-r--r--src/test/test/javassist/bytecode/analysis/DomTreeTest.java113
-rw-r--r--src/test/test/javassist/bytecode/analysis/ScannerTest.java2
-rw-r--r--src/test/test/javassist/convert/ArrayAccessReplaceTest.java55
-rw-r--r--src/test/test/javassist/proxy/Foo.java16
-rw-r--r--src/test/test/javassist/proxy/JBPAPP9257Test.java68
-rw-r--r--src/test/test/javassist/proxy/ProxyCacheGCTest.java13
-rw-r--r--src/test/test/javassist/proxy/ProxyFactoryCompatibilityTest.java15
-rw-r--r--src/test/test/javassist/proxy/ProxySerializationTest.java9
-rw-r--r--src/test/test/javassist/proxy/ProxySimpleTest.java337
-rw-r--r--src/test/test/javassist/tools/DefineClassCapability.java7
-rw-r--r--src/test/test/javassist/tools/DummyClass.java9
-rw-r--r--src/test/test1/AddClassInfo.java7
-rw-r--r--src/test/test1/ArrayAccess.java17
-rw-r--r--src/test/test1/BenchProceed.java83
-rw-r--r--src/test/test1/BenchProceedNew.java74
-rw-r--r--src/test/test1/BenchStaticMethod.java53
-rw-r--r--src/test/test1/CalleeAfter.java24
-rw-r--r--src/test/test1/CalleeAfter2.java50
-rw-r--r--src/test/test1/CalleeAfter3.java39
-rw-r--r--src/test/test1/CalleeBefore.java39
-rw-r--r--src/test/test1/CalleeCatch.java17
-rw-r--r--src/test/test1/Cflow.java40
-rw-r--r--src/test/test1/Clinit.java8
-rw-r--r--src/test/test1/Clinit2.java8
-rw-r--r--src/test/test1/CodeConv.java25
-rw-r--r--src/test/test1/DefineClassCapability.java7
-rw-r--r--src/test/test1/Delegator.java12
-rw-r--r--src/test/test1/Dispatch.java15
-rw-r--r--src/test/test1/DollarClass.java21
-rw-r--r--src/test/test1/EmptyBody.java20
-rw-r--r--src/test/test1/ExprEdit.java11
-rw-r--r--src/test/test1/ExprEdit2.java8
-rw-r--r--src/test/test1/ExprEdit3.java13
-rw-r--r--src/test/test1/ExprEdit4.java16
-rw-r--r--src/test/test1/ExprEdit5.java14
-rw-r--r--src/test/test1/ExprEdit6.java15
-rw-r--r--src/test/test1/ExprEdit7.java30
-rw-r--r--src/test/test1/ExprEdit8.java16
-rw-r--r--src/test/test1/FieldInit.java24
-rw-r--r--src/test/test1/FieldInit2.java21
-rw-r--r--src/test/test1/FieldMod.java7
-rw-r--r--src/test/test1/Freeze.java4
-rw-r--r--src/test/test1/GetThrowables.java47
-rw-r--r--src/test/test1/Handler.java29
-rw-r--r--src/test/test1/Howard.java30
-rw-r--r--src/test/test1/InvokeInt.java19
-rw-r--r--src/test/test1/LineNumber.java46
-rw-r--r--src/test/test1/LocalVars.java18
-rw-r--r--src/test/test1/MakeClass.java9
-rw-r--r--src/test/test1/MySerializableClass.java36
-rw-r--r--src/test/test1/NewInterface.java5
-rw-r--r--src/test/test1/Pac.java15
-rw-r--r--src/test/test1/Point.java5
-rw-r--r--src/test/test1/Proceed.java12
-rw-r--r--src/test/test1/Proceed2.java26
-rw-r--r--src/test/test1/Proceed3.java9
-rw-r--r--src/test/test1/RenameClass.java27
-rw-r--r--src/test/test1/SetBody.java9
-rw-r--r--src/test/test1/SetName.java6
-rw-r--r--src/test/test1/SigType.java21
-rw-r--r--src/test/test1/StaticConsBody.java18
-rw-r--r--src/test/test1/StaticField.java5
-rw-r--r--src/test/test1/Subtype.java17
-rw-r--r--src/test/test1/TryCatch.java34
-rw-r--r--src/test/test2/AddCatchForConstructor.classbin0 -> 488 bytes
-rw-r--r--src/test/test2/AddCatchForConstructor.java13
-rw-r--r--src/test/test2/AddLocalVar.classbin0 -> 395 bytes
-rw-r--r--src/test/test2/AddLocalVar.java8
-rw-r--r--src/test/test2/AddMethod.java10
-rw-r--r--src/test/test2/Anon.java19
-rw-r--r--src/test/test2/ArrayAndNull.java9
-rw-r--r--src/test/test2/ArrayInit.classbin0 -> 465 bytes
-rw-r--r--src/test/test2/ArrayLenTest.classbin0 -> 366 bytes
-rw-r--r--src/test/test2/ArrayLenTest.java5
-rw-r--r--src/test/test2/ArrayLength.classbin0 -> 238 bytes
-rw-r--r--src/test/test2/Brennan.classbin0 -> 471 bytes
-rw-r--r--src/test/test2/Brennan.java6
-rw-r--r--src/test/test2/CodeGen.classbin0 -> 2052 bytes
-rw-r--r--src/test/test2/CodeGen.java15
-rw-r--r--src/test/test2/CodeGen2.classbin0 -> 700 bytes
-rw-r--r--src/test/test2/ConstBody.classbin0 -> 500 bytes
-rw-r--r--src/test/test2/ConstBody.java22
-rw-r--r--src/test/test2/ConstField.classbin0 -> 578 bytes
-rw-r--r--src/test/test2/ConstField.java13
-rw-r--r--src/test/test2/Construct.java7
-rw-r--r--src/test/test2/DefineClassCapability.java7
-rw-r--r--src/test/test2/DotClass.classbin0 -> 1337 bytes
-rw-r--r--src/test/test2/DotClass2.classbin0 -> 578 bytes
-rw-r--r--src/test/test2/DotClass4.classbin0 -> 810 bytes
-rw-r--r--src/test/test2/Finally.classbin0 -> 1125 bytes
-rw-r--r--src/test/test2/Finally.java12
-rw-r--r--src/test/test2/Imported.classbin0 -> 166 bytes
-rw-r--r--src/test/test2/Importer.classbin0 -> 401 bytes
-rw-r--r--src/test/test2/IncOp.classbin0 -> 221 bytes
-rw-r--r--src/test/test2/Inherit.classbin0 -> 991 bytes
-rw-r--r--src/test/test2/Inherit.java26
-rw-r--r--src/test/test2/Inner.classbin0 -> 1411 bytes
-rw-r--r--src/test/test2/Inner.java14
-rw-r--r--src/test/test2/InsertAt.classbin0 -> 686 bytes
-rw-r--r--src/test/test2/InsertAt.java23
-rw-r--r--src/test/test2/InsertLocal.classbin0 -> 1088 bytes
-rw-r--r--src/test/test2/InsertLocal.java33
-rw-r--r--src/test/test2/LocalVar.classbin0 -> 550 bytes
-rw-r--r--src/test/test2/LocalVar.java6
-rw-r--r--src/test/test2/MakeStaticMethod.classbin0 -> 281 bytes
-rw-r--r--src/test/test2/MethodCall.classbin0 -> 475 bytes
-rw-r--r--src/test/test2/MethodCall.java8
-rw-r--r--src/test/test2/Nested$Inner3.classbin0 -> 593 bytes
-rw-r--r--src/test/test2/Nested.classbin0 -> 904 bytes
-rw-r--r--src/test/test2/Nested.java23
-rw-r--r--src/test/test2/Nested2$Inner.classbin0 -> 1081 bytes
-rw-r--r--src/test/test2/Nested2.classbin0 -> 1230 bytes
-rw-r--r--src/test/test2/Nested2.java16
-rw-r--r--src/test/test2/Nested3$Inner.classbin0 -> 678 bytes
-rw-r--r--src/test/test2/Nested3.classbin0 -> 944 bytes
-rw-r--r--src/test/test2/Nested3.java17
-rw-r--r--src/test/test2/Nested4$Inner.classbin0 -> 301 bytes
-rw-r--r--src/test/test2/Nested4.classbin0 -> 482 bytes
-rw-r--r--src/test/test2/Nested4.java6
-rw-r--r--src/test/test2/NewArray.classbin0 -> 864 bytes
-rw-r--r--src/test/test2/NewArray.java19
-rw-r--r--src/test/test2/NewExprInTry.classbin0 -> 681 bytes
-rw-r--r--src/test/test2/NewExprInTry.java29
-rw-r--r--src/test/test2/NewExprTry.classbin0 -> 948 bytes
-rw-r--r--src/test/test2/NewExprTry.java21
-rw-r--r--src/test/test2/NewOp.classbin0 -> 855 bytes
-rw-r--r--src/test/test2/NewOp.java16
-rw-r--r--src/test/test2/NullArgTest.classbin0 -> 426 bytes
-rw-r--r--src/test/test2/PrivateMethod.java11
-rw-r--r--src/test/test2/Prune.java17
-rw-r--r--src/test/test2/Remove.classbin0 -> 445 bytes
-rw-r--r--src/test/test2/Remove.java25
-rw-r--r--src/test/test2/RemoveCall.classbin0 -> 539 bytes
-rw-r--r--src/test/test2/RemoveCall.java13
-rw-r--r--src/test/test2/ReplaceClassName.classbin0 -> 538 bytes
-rw-r--r--src/test/test2/ReplaceClassName.java18
-rw-r--r--src/test/test2/SetExceptions.classbin0 -> 387 bytes
-rw-r--r--src/test/test2/SetExceptions.java7
-rw-r--r--src/test/test2/SetSuper.classbin0 -> 218 bytes
-rw-r--r--src/test/test2/SetSuperIntf.classbin0 -> 134 bytes
-rw-r--r--src/test/test2/SetSuperParent.classbin0 -> 178 bytes
-rw-r--r--src/test/test2/StaticArraysMem.java5
-rw-r--r--src/test/test2/StaticFinal.classbin0 -> 608 bytes
-rw-r--r--src/test/test2/StaticMember.classbin0 -> 863 bytes
-rw-r--r--src/test/test2/StaticMember.java11
-rw-r--r--src/test/test2/StaticMember2.classbin0 -> 888 bytes
-rw-r--r--src/test/test2/StaticMember2.java12
-rw-r--r--src/test/test2/SuperCall.classbin0 -> 628 bytes
-rw-r--r--src/test/test2/SuperCall.java20
-rw-r--r--src/test/test2/SuperInterface1.java6
-rw-r--r--src/test/test2/SuperInterface2.java3
-rw-r--r--src/test/test2/SuperInterface3.classbin0 -> 349 bytes
-rw-r--r--src/test/test2/Switch.classbin0 -> 784 bytes
-rw-r--r--src/test/test2/Synch.classbin0 -> 503 bytes
-rw-r--r--src/test/test2/Where.java10
-rw-r--r--src/test/test3/Anno.java15
-rw-r--r--src/test/test3/Anno2.java11
-rw-r--r--src/test/test3/Anno3.java17
-rw-r--r--src/test/test3/Anno6.java6
-rw-r--r--src/test/test3/AnnoTest.java21
-rw-r--r--src/test/test3/AnnoTest6.java6
-rw-r--r--src/test/test3/CheckModify.java5
-rw-r--r--src/test/test3/ChibaAnnotation.java11
-rw-r--r--src/test/test3/ColorName.java5
-rw-r--r--src/test/test3/Constructor.java37
-rw-r--r--src/test/test3/Constructor2.java7
-rw-r--r--src/test/test3/CopyAnno.java10
-rw-r--r--src/test/test3/DefineClassCapability.java7
-rw-r--r--src/test/test3/EmptyCatch.java14
-rw-r--r--src/test/test3/EmptyConstructor.java33
-rw-r--r--src/test/test3/Enhancer.java181
-rw-r--r--src/test/test3/Erasure.java16
-rw-r--r--src/test/test3/FieldAccessType.java11
-rw-r--r--src/test/test3/FieldCopy.java12
-rw-r--r--src/test/test3/Frozen.java7
-rw-r--r--src/test/test3/GetMethods.java32
-rw-r--r--src/test/test3/InheritCons.java9
-rw-r--r--src/test/test3/InnerClass.java7
-rw-r--r--src/test/test3/InnerMethod.java17
-rw-r--r--src/test/test3/InsParam.java16
-rw-r--r--src/test/test3/InsertAfter.java17
-rw-r--r--src/test/test3/InsertBeforeType.java11
-rw-r--r--src/test/test3/InvokeArray.java15
-rw-r--r--src/test/test3/InvokeIntf.java30
-rw-r--r--src/test/test3/JIRA63.java5
-rw-r--r--src/test/test3/JIRA63Helper.java7
-rw-r--r--src/test/test3/MethodRedirect.java21
-rw-r--r--src/test/test3/MethodRedirect2.java43
-rw-r--r--src/test/test3/Mods.java9
-rw-r--r--src/test/test3/Name.java17
-rw-r--r--src/test/test3/NestedClass.java25
-rw-r--r--src/test/test3/NewClass2.java5
-rw-r--r--src/test/test3/NewExprTryCatch.java30
-rw-r--r--src/test/test3/PackName.java12
-rw-r--r--src/test/test3/ParamAnno.java9
-rw-r--r--src/test/test3/PublicInner.java13
-rw-r--r--src/test/test3/RecReplace.java20
-rw-r--r--src/test/test3/RecReplace2.java19
-rw-r--r--src/test/test3/ReplaceNew.java20
-rw-r--r--src/test/test3/SetModifiers.java8
-rw-r--r--src/test/test3/SigAttribute.java6
-rw-r--r--src/test/test3/StrBuild.java8
-rw-r--r--src/test/test3/SubValue.java15
-rw-r--r--src/test/test3/Superclass.java15
-rw-r--r--src/test/test3/Switch.java30391
-rw-r--r--src/test/test3/TransNewClass.java37
-rw-r--r--src/test/test3/TransformRead.java15
-rw-r--r--src/test/test3/Unique.java24
-rw-r--r--src/test/test3/Visible.java17
-rw-r--r--src/test/test3/VisibleAnno.java7
-rw-r--r--src/test/test3/VoidReturn.java5
-rw-r--r--src/test/test3/sub/SubPackName.java5
-rw-r--r--src/test/test3/sub/Visible.java12
-rw-r--r--src/test/test4/Aaload.java15
-rw-r--r--src/test/test4/AfterTest.java119
-rw-r--r--src/test/test4/Anno.java6
-rw-r--r--src/test/test4/Anno1.java5
-rw-r--r--src/test/test4/AnnoArg.java18
-rw-r--r--src/test/test4/AnnoLoad.java5
-rw-r--r--src/test/test4/CodeConv.java19
-rw-r--r--src/test/test4/CodeConv2.java39
-rw-r--r--src/test/test4/CtArrayTest.java4
-rw-r--r--src/test/test4/DeclMethodsList.java7
-rw-r--r--src/test/test4/DefineClassCapability.java7
-rw-r--r--src/test/test4/GapSwitch.java83
-rw-r--r--src/test/test4/GetAllRef.java33
-rw-r--r--src/test/test4/GetAllRefAnno2.java6
-rw-r--r--src/test/test4/GetAllRefEnum2.java3
-rw-r--r--src/test/test4/GetAllRefInnerTest.java29
-rw-r--r--src/test/test4/GetFieldDesc.java11
-rw-r--r--src/test/test4/ImportPac.java4
-rw-r--r--src/test/test4/InvokeDyn.java26
-rw-r--r--src/test/test4/JIRA152.java60
-rw-r--r--src/test/test4/JIRA158.java30
-rw-r--r--src/test/test4/JIRA166.java12
-rw-r--r--src/test/test4/JIRA181.java32
-rw-r--r--src/test/test4/JIRA181b.java12
-rw-r--r--src/test/test4/JIRA186.java7
-rw-r--r--src/test/test4/JIRA195.java31
-rw-r--r--src/test/test4/JIRA207.java51
-rw-r--r--src/test/test4/JIRA212.java23
-rw-r--r--src/test/test4/JIRA220.java13
-rw-r--r--src/test/test4/JIRA93.java5
-rw-r--r--src/test/test4/LocalVars.java60
-rw-r--r--src/test/test4/Lvtt.java11
-rw-r--r--src/test/test4/MakeMethod.java7
-rw-r--r--src/test/test4/MethodParamTest.java5
-rw-r--r--src/test/test4/MultiCatch.java23
-rw-r--r--src/test/test4/NestedClass.java29
-rw-r--r--src/test/test4/NewImportPac.java4
-rw-r--r--src/test/test4/NewRemover.java30
-rw-r--r--src/test/test4/Rename.java30
-rw-r--r--src/test/test4/Signature.java16
-rw-r--r--src/test/test4/VarArgs.java15
-rw-r--r--src/test/test4/length.java5
-rw-r--r--src/test/test5/BoolTest.java15
-rw-r--r--src/test/test5/DefaultMethod.java19
-rw-r--r--src/test/test5/DefineClassCapability.java7
-rw-r--r--src/test/test5/Entity.java9
-rw-r--r--src/test/test5/InnerClassRemove.java8
-rw-r--r--src/test/test5/InnerModifier.java6
-rw-r--r--src/test/test5/InnerModifier2.java13
-rw-r--r--src/test/test5/InvalidCastDollar.java11
-rw-r--r--src/test/test5/Issue155.java22
-rw-r--r--src/test/test5/JIRA241.java33
-rw-r--r--src/test/test5/JIRA242.java25
-rw-r--r--src/test/test5/JIRA246.java21
-rw-r--r--src/test/test5/JIRA248.java22
-rw-r--r--src/test/test5/JIRA250Super.java17
-rw-r--r--src/test/test5/JIRA250Super2.java5
-rw-r--r--src/test/test5/JavassistInvalidCastTest.java6
-rw-r--r--src/test/test5/NestHost.java11
-rw-r--r--src/test/test5/NestHost2.java11
-rw-r--r--src/test/test5/ProceedDefault.java13
-rw-r--r--src/test/test5/RemoveAnnotation.java20
-rw-r--r--src/test/test5/StackmapWithArray83.java38
-rw-r--r--src/test/test5/TypeAnno.java30
-rw-r--r--src/test/test5/TypeAnnoA.java10
-rw-r--r--src/test/test5/VarArgsMethod.java6
-rw-r--r--src/test/testproxy/BridgeMethod.java19
-rw-r--r--src/test/testproxy/ProxyFactoryPerformanceTest.java200
-rw-r--r--src/test/testproxy/ProxyTester.java477
-rw-r--r--src/test/testproxy/Target.java17
-rw-r--r--src/test/testproxy/Target1.java5
-rw-r--r--src/test/testproxy/Target127.java20
-rw-r--r--src/test/testproxy/Target189.java74
-rw-r--r--src/test/testproxy/Target2.java18
-rw-r--r--src/test/testproxy/Target3.java6
-rw-r--r--src/test/testproxy/Target4.java9
-rw-r--r--src/test/testproxy/Target5.java7
-rw-r--r--src/test/testproxy/TargetInit.java10
-rw-r--r--src/test/testproxy/sub/TargetSuper.java8
-rw-r--r--tutorial/tutorial.html12
-rw-r--r--tutorial/tutorial2.html15
-rw-r--r--tutorial/tutorial3.html117
590 files changed, 57861 insertions, 5117 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1f83f06
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+.idea/
+bin/
+eclipse-output/
+target/
+runtest/
+html/
+lib/
+tmp/
+
+.DS_Store
+.classpath
+.project
+.settings
+TestLog.xml
+*~
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..e8e9200
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,43 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["external_javassist_license"],
+}
+
+// Added automatically by a large-scale-change
+//
+// large-scale-change filtered out the below license kinds as false-positives:
+// SPDX-license-identifier-LGPL-2.1
+// SPDX-license-identifier-MPL
+// SPDX-license-identifier-MPL-1.1
+// See: http://go/android-license-faq
+license {
+ name: "external_javassist_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "LICENSE",
+ ],
+}
+
+java_library_host {
+ name: "javassist",
+
+ srcs: [
+ "src/main/**/*.java",
+ ],
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index d265e3f..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-# We need classes from the tools dir in the JDK
-LOCAL_CLASSPATH := $(HOST_JDK_TOOLS_JAR)
-
-LOCAL_MODULE := javassist
-
-LOCAL_SRC_FILES := $(call all-java-files-under,src/main)
-
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/HOWTO.txt b/HOWTO.txt
deleted file mode 100644
index 91f9992..0000000
--- a/HOWTO.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-In order to update the code, simply export it from svn with the regenerate_from_source.sh.
-If you want to run the tests, remember to do it from master or a branch that
-includes junit.jar and that has been compiled. The command is:
-ant -propertyfile build.properties test
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/License.html b/License.html
index bec1335..7d842b4 100644
--- a/License.html
+++ b/License.html
@@ -337,7 +337,7 @@ MISCELLANEOUS.</B>
basis. Nothing herein is intended or shall be deemed to constitute any
admission of liability.</UL><B>13. MULTIPLE-LICENSED CODE.</B>
<UL>Initial Developer may designate portions of the Covered Code as
- �Multiple-Licensed?.&nbsp; �Multiple-Licensed? means that the Initial
+ "Multiple-Licensed".&nbsp; "Multiple-Licensed" means that the Initial
Developer permits you to utilize portions of the Covered Code under Your
choice of the MPL or the alternative licenses, if any, specified by the
Initial Developer in the file described in Exhibit A.</UL>
@@ -353,19 +353,20 @@ MISCELLANEOUS.</B>
<P>The Original Code is Javassist.
<P>The Initial Developer of the Original Code is Shigeru Chiba.
Portions created by the Initial Developer are<BR>&nbsp;
- Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.
- <P>Contributor(s): ______________________________________.
+ Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ <P>Contributor(s): __Bill Burke, Jason T. Greene______________.
- <P>Alternatively, the contents of this file may be used under the terms of
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the LGPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of either the MPL or the LGPL.
+<p>Alternatively, the contents of this software may be used under the
+terms of the GNU Lesser General Public License Version 2.1 or later
+(the "LGPL"), or the Apache License Version 2.0 (the "AL"),
+in which case the provisions of the LGPL or the AL are applicable
+instead of those above. If you wish to allow use of your version of
+this software only under the terms of either the LGPL or the AL, and not to allow others to
+use your version of this software under the terms of the MPL, indicate
+your decision by deleting the provisions above and replace them with
+the notice and other provisions required by the LGPL or the AL. If you do not
+delete the provisions above, a recipient may use your version of this
+software under the terms of any one of the MPL, the LGPL or the AL.
<P></P></UL>
</BODY>
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d8c341c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,18 @@
+name: "javassist"
+description:
+ "Java bytecode engineering toolkit"
+
+third_party {
+ url {
+ type: HOMEPAGE
+ value: "https://www.javassist.org/"
+ }
+ url {
+ type: GIT
+ value: "https://github.com/jboss-javassist/javassist.git"
+ }
+ version: "rel_3_27_0_ga"
+ last_upgrade_date { year: 2020 month: 9 day: 10 }
+ // The project can be licensed under one of MPL, LGPL or AL, and Apache 2.0 is a "notice" license.
+ license_type: NOTICE
+}
diff --git a/MODULE_LICENSE_MPL b/MODULE_LICENSE_APACHE2
index e69de29..e69de29 100644
--- a/MODULE_LICENSE_MPL
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index d0d944c..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,25 +0,0 @@
-EXHIBIT A -Mozilla Public License.
- The contents of this file are subject to the Mozilla Public License
- Version 1.1 (the "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
- The Original Code is Javassist.
- The Initial Developer of the Original Code is Shigeru Chiba.
- Portions created by the Initial Developer are<BR>&nbsp;
- Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.
- Contributor(s): ______________________________________.
-
- Alternatively, the contents of this file may be used under the terms of
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the LGPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of either the MPL or the LGPL.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5e28e58
--- /dev/null
+++ b/README.md
@@ -0,0 +1,40 @@
+Java bytecode engineering toolkit
+### [Javassist version 3](http://www.javassist.org)
+
+Copyright (C) 1999-2018 by Shigeru Chiba, All rights reserved.
+
+Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation
+simple. It is a class library for editing bytecodes in Java; it enables Java
+programs to define a new class at runtime and to modify a class file when the
+JVM loads it. Unlike other similar bytecode editors, Javassist provides two
+levels of API: source level and bytecode level. If the users use the source-
+level API, they can edit a class file without knowledge of the specifications
+of the Java bytecode. The whole API is designed with only the vocabulary of
+the Java language. You can even specify inserted bytecode in the form of
+source text; Javassist compiles it on the fly. On the other hand, the
+bytecode-level API allows the users to directly edit a class file as other
+editors.
+
+This software is distributed under the Mozilla Public License Version 1.1,
+the GNU Lesser General Public License Version 2.1 or later, or
+the Apache License Version 2.0.
+
+#### Files
+
+ * [Readme.html](Readme.html)
+Readme file (a longer version of this file).
+
+ * [License.html](License.html)
+License file
+
+ * [tutorial/tutorial.html](tutorial/tutorial.html)
+Tutorial
+
+ * ./javassist.jar
+The Javassist jar file (class files)
+
+ * ./src/main
+The source files
+
+ * [html/index.html](html/index.html)
+The top page of the Javassist API document.
diff --git a/Readme.html b/Readme.html
index f6429fe..793677d 100644
--- a/Readme.html
+++ b/Readme.html
@@ -7,7 +7,7 @@
<h1>Javassist version 3</h1>
-<h3>Copyright (C) 1999-2010 by Shigeru Chiba, All rights reserved.</h3>
+<h3>Copyright (C) 1999-2018 by Shigeru Chiba, All rights reserved.</h3>
<p><br></p>
@@ -32,7 +32,7 @@ other editors.
<table>
<tr>
<td><li><tt><a href="License.html">License.html</a></tt></td>
-<td>License file
+<td>License file (MPL/LGPL/Apache triple license)
(Also see the <a href="#copyright">copyright notices</a> below)</td>
</tr>
@@ -281,6 +281,103 @@ see javassist.Dump.
<h2>Changes</h2>
+<p>-version 3.24.1 on December 9, 2018
+<ul>
+ <li>GitHub Issue #228, #229</li>
+<ul>
+</p>
+
+<p>-version 3.24 on November 1, 2018
+<ul>
+ <li>Java 11 supports.</li>
+ <li>JIRA JASSIST-267.</li>
+ <li>Github PR #218.</li>
+</ul>
+</p>
+
+<p>-version 3.23.1 on July 2, 2018
+<ul>
+ <li>Github PR #171.</li>
+</ul>
+</p>
+
+<p>-version 3.23 on June 21, 2018
+
+<ul>
+ <li>Fix leaking file handlers in ClassPool and removed ClassPath.close(). Github issue #165.
+</ul>
+</p>
+
+<p>-version 3.22 on October 10, 2017
+
+<ul>
+<li>Java 9 supports.
+<li>JIRA JASSIST-261.
+</ul>
+</p>
+
+<p>-version 3.21 on October 4, 2016
+<ul>
+<li>JIRA JASSIST-244, 245, 248, 250, 255, 256, 259, 262.
+<li><code>javassist.tools.Callback</code> was modified to be Java 1.4 compatible.
+The parameter type of <code>Callback#result()</code> was changed.
+<li>The algorithm for generating a stack-map table was modified to fix github issue #83.
+<li>A bug of ProxyFactory related to default methods was fixed. It is github issue #45.
+</ul>
+</p>
+
+<p>-version 3.20 on June 25, 2015
+<ul>
+<li>JIRA JASSIST-241, 242, 246.
+</ul>
+</p>
+
+
+<p>-version 3.19 on January 6, 2015
+<ul>
+<li>JIRA JASSIST-158, 205, 206, 207, 208, 209, 211, 212, 216, 220, 223, 224,
+ 227, 230, 234, 235, 236, 237, 238, 240.
+</ul>
+</p>
+
+<p>-version 3.18 on June 3, 2013
+<ul>
+<li>The source code repository has been moved to <a href="https://github.com/jboss-javassist/javassist">GitHub</a></li>.
+
+<li>JIRA JASSIST-181, 183, 184, 189, 162, 185, 186, 188, 190, 195, 199, 201.
+</ul>
+
+<p>-version 3.17.1 on December 3, 2012
+<ul>
+ <li>JIRA JASSIST-177, 178, 182.
+</ul>
+
+
+<p>-version 3.17 on November 8, 2012
+<ul>
+ <li>OSGi bundle info is now included in the jar file.
+ <li>A stackmap generator has been rewritten.
+ <li>JIRA JASSIST-160, 163, 166, 168, 170, 171, 174, 175, 176 have been fixed.
+</ul>
+
+<p>-version 3.16.1 on March 6, 2012
+<ul>
+ <li>Maven now works. JIRA JASSIST-44, 106, 156 have been fixed.
+</ul>
+
+<p>-version 3.16 on February 19, 2012
+<ul>
+ <li>JIRA JASSIST-126, 127, 144, 145, 146, 147, 149, 150, 151, 152, 153, 155.
+ <li><code>javassist.bytecode.analysis.ControlFlow</code> was added.
+ <li>Java 7 compatibility.
+</ul>
+
+<p>-version 3.15 on July 8, 2011
+<ul>
+ <li>The license was changed to MPL/LGPL/Apache triple.
+ <li>JIRA JASSIST-138 and 142 were fixed.
+</ul>
+
<p>-version 3.14 on October 5, 2010
<ul>
@@ -703,31 +800,11 @@ see javassist.Dump.
<p><br>
-<h2>Bug reports etc.</h2>
-
-<dl>
-<dt>Bug reports:
-<dd>Post your reports at <a href="http://www.jboss.org/jive.jsp">Forums</a>
-or directly send an email to:
-<br>&nbsp;
-<tt><a href="mailto:chiba@acm.org">chiba@acm.org</a></tt>
-or
-<tt><a href="mailto:chiba@is.titech.ac.jp">chiba@is.titech.ac.jp</a></tt>
-<br>
-
-<p><dt>The home page of Javassist:
-<dd>Visit <a href="http://www.javassist.org"><tt>www.javassist.org</tt></a>
-and <a href="http://www.jboss.org/index.html?module=html&op=userdisplay&id=developers/projects/javassist"><tt>www.jboss.org</tt></a>
-
-</dl>
-
-<p><br>
-
<a name="copyright">
<h2>Copyright notices</h2>
<p>Javassist, a Java-bytecode translator toolkit.
-<br>Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.
+<br>Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
<p>The contents of this software, Javassist, are subject to
the Mozilla Public License Version 1.1 (the "License");<br>
@@ -744,26 +821,27 @@ See the License for the specific language governing rights and
<p>The Initial Developer of the Original Code is Shigeru Chiba.
Portions created by the Initial Developer are<br>&nbsp;
-Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.
-<p>Contributor(s): ______________________________________.
+Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+<p>Contributor(s): __Bill Burke, Jason T. Greene______________.
<p>Alternatively, the contents of this software may be used under the
terms of the GNU Lesser General Public License Version 2.1 or later
-(the "LGPL"), in which case the provisions of the LGPL are applicable
+(the "LGPL"), or the Apache License Version 2.0 (the "AL"),
+in which case the provisions of the LGPL or the AL are applicable
instead of those above. If you wish to allow use of your version of
-this software only under the terms of the LGPL, and not to allow others to
+this software only under the terms of either the LGPL or the AL, and not to allow others to
use your version of this software under the terms of the MPL, indicate
your decision by deleting the provisions above and replace them with
-the notice and other provisions required by the LGPL. If you do not
+the notice and other provisions required by the LGPL or the AL. If you do not
delete the provisions above, a recipient may use your version of this
-software under the terms of either the MPL or the LGPL.
+software under the terms of any one of the MPL, the LGPL or the AL.
<p>If you obtain this software as part of JBoss, the contents of this
software may be used under only the terms of the LGPL. To use them
under the MPL, you must obtain a separate package including only
Javassist but not the other part of JBoss.
-<p>All the contributors to the original source tree must agree to
+<p>All the contributors to the original source tree have agreed to
the original license term described above.
<p><br>
@@ -788,13 +866,14 @@ Bruce McDonald, Mark Brennan, Vlad Skarzhevskyy,
Brett Randall, Tsuyoshi Murakami, Nathan Meyers, Yoshiyuki Usui
Yutaka Sunaga, Arjan van der Meer, Bruce Eckel, Guillaume Pothier,
Kumar Matcha, Andreas Salathe, Renat Zubairov, Armin Haaf,
-Emmanuel Bernard
+Emmanuel Bernard, Jason T. Greene
and all other contributors for their contributions.
<p><br>
<hr>
-<a href="http://www.is.titech.ac.jp/~chiba">Shigeru Chiba</a>
-(Email: <tt>chiba@acm.org</tt>)
-<br>Dept. of Math. and Computing Sciences,
-<a href="http://www.titech.ac.jp">Tokyo Institute of Technology</a>
+<a href="http://www.javassist.org">Shigeru Chiba</a>
+(Email: <tt>chiba@javassist.org</tt>)
+
+</body>
+</html>
diff --git a/build.properties b/build.properties
deleted file mode 100644
index 42e774e..0000000
--- a/build.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-# We only use junit, so we set the lib.dir to the junit out dir; this
-# works only on full builds that include master and in unix (though
-# you can change the path in your local repository).
-lib.dir=../../out/host/linux-x86/framework
diff --git a/build.xml b/build.xml
index 9d4c329..c0178ee 100644
--- a/build.xml
+++ b/build.xml
@@ -6,18 +6,20 @@
<project name="javassist" default="jar" basedir=".">
- <property name="dist-version" value="javassist-3.14.0-GA"/>
+ <property name="dist-version" value="javassist-3.24.1-GA"/>
<property environment="env"/>
<property name="target.jar" value="javassist.jar"/>
<property name="target-src.jar" value="javassist-src.jar"/>
<property name="lib.dir" value="${basedir}/lib"/>
<property name="src.dir" value="${basedir}/src/main"/>
- <property name="build.dir" value="${basedir}/build"/>
+ <property name="build.dir" value="${basedir}/target"/>
<property name="build.classes.dir" value="${build.dir}/classes"/>
<property name="test.src.dir" value="${basedir}/src/test"/>
- <property name="test.build.dir" value="${basedir}/build/test-classes"/>
- <property name="test.reports.dir" value = "${basedir}/build/test-output"/>
+ <property name="test.lib.dir" value="${test.src.dir}/resources"/>
+ <property name="test.build.dir" value="${build.dir}/test-classes"/>
+ <property name="test.run.dir" value="${build.dir}/runtest"/>
+ <property name="test.reports.dir" value = "${build.dir}/test-output"/>
<property name="run.dir" value="${build.classes.dir}"/>
@@ -31,13 +33,16 @@
<path id="test.compile.classpath">
<pathelement location="${build.classes.dir}"/>
<pathelement location="${lib.dir}/junit.jar"/>
+ <pathelement location="${lib.dir}/hamcrest.jar"/>
</path>
<property name="test.compile.classpath" refid="test.compile.classpath"/>
<path id="test.classpath">
<pathelement location="${test.build.dir}"/>
+ <pathelement location="${test.lib.dir}"/>
<pathelement location="${lib.dir}/junit.jar"/>
+ <pathelement location="${lib.dir}/hamcrest.jar"/>
<pathelement location="${build.classes.dir}"/>
</path>
@@ -50,7 +55,8 @@
<mkdir dir="${build.dir}"/>
<mkdir dir="${build.classes.dir}"/>
<mkdir dir="${test.build.dir}"/>
- <mkdir dir="${test.reports.dir}"/>
+ <mkdir dir="${test.reports.dir}"/>
+ <mkdir dir="${test.run.dir}"/>
</target>
<!-- =================================================================== -->
@@ -62,46 +68,66 @@
debug="on"
deprecation="on"
optimize="off"
+ includeantruntime="true"
includes="**">
<classpath refid="classpath"/>
</javac>
</target>
- <target name="compile14" depends="prepare">
+ <target name="compile18" depends="prepare">
<javac srcdir="${src.dir}"
destdir="${build.classes.dir}"
debug="on"
deprecation="on"
- source="1.4"
- target="1.4"
+ source="1.7"
+ target="1.7"
optimize="off"
+ includeantruntime="true"
includes="**">
<classpath refid="classpath"/>
</javac>
</target>
-
+
<target name="test-compile" depends="compile">
<javac srcdir="${test.src.dir}"
destdir="${test.build.dir}"
debug="on"
deprecation="on"
optimize="off"
+ includeantruntime="false"
includes="**">
<classpath refid="test.compile.classpath"/>
+ <compilerarg value="-parameters" />
</javac>
</target>
- <target name="test" depends="test-compile">
- <junit fork="true" printsummary="true">
+ <target name="runtest" depends="jar,test-compile">
+ <copy file="${test.lib.dir}/empty.jar"
+ tofile="${test.lib.dir}/emptyorig.jar"
+ preservelastmodified="true" />
+ <junit fork="true" printsummary="true" dir="${test.run.dir}">
+ <jvmarg value="-XX:-FailOverToOldVerifier"/>
+ <classpath refid="test.classpath"/>
+ <formatter type="xml" extension=".xml"/>
+ <test name="javassist.JvstTest" outfile="TestLog" />
+ </junit>
+ <move file="${test.lib.dir}/emptyorig.jar"
+ tofile="${test.lib.dir}/empty.jar" />
+ </target>
+
+ <target name="runtest9" depends="jar,test-compile">
+ <copy file="${test.lib.dir}/empty.jar"
+ tofile="${test.lib.dir}/emptyorig.jar"
+ preservelastmodified="true" />
+ <junit fork="true" printsummary="true" dir="${test.run.dir}">
+ <jvmarg line="--add-opens java.base/java.lang=ALL-UNNAMED" />
+ <jvmarg value="-XX:-FailOverToOldVerifier"/>
<classpath refid="test.classpath"/>
- <formatter type="plain"/>
- <formatter type="xml"/>
- <batchtest todir="${test.reports.dir}">
- <fileset dir="${test.build.dir}">
- <include name="**/*Test.*"/>
- </fileset>
- </batchtest>
+ <formatter type="xml" extension=".xml"/>
+ <test name="javassist.JvstTest" outfile="TestLog" />
</junit>
+ <move file="${test.lib.dir}/emptyorig.jar"
+ tofile="${test.lib.dir}/empty.jar" />
</target>
<target name="sample" depends="compile">
@@ -155,8 +181,8 @@
to ${build.classes.dir}.</echo>
</target>
- <target name="jar" depends="compile14">
- <jar jarfile="${target.jar}" manifest="${src.dir}/META-INF/MANIFEST.MF">
+ <target name="jar" depends="compile18">
+ <jar jarfile="${target.jar}" update="true" manifest="${src.dir}/META-INF/MANIFEST.MF">
<fileset dir="${build.classes.dir}">
<include name="**/*.class"/>
</fileset>
@@ -185,12 +211,13 @@ to ${build.classes.dir}.</echo>
windowtitle="Javassist API">
<doctitle><![CDATA[<h1>Javassist</h1>]]></doctitle>
<bottom><![CDATA[<i>Javassist, a Java-bytecode translator toolkit.<br>
-Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.</i>]]></bottom>
+Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.</i>]]></bottom>
</javadoc>
</target>
<target name="dist" depends="jar,javadocs">
- <delete file="${dist-version}.zip"/>
+ <delete file="${dist-version}.zip"/>
+ <delete file="TestLog.xml"/>
<zip zipfile="${dist-version}.zip">
<zipfileset dir="${basedir}" prefix="${dist-version}">
<include name="html/**"/>
@@ -205,8 +232,8 @@ Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.</i>]]></bottom>
</target>
<target name="clean">
- <delete dir="build"/>
<delete dir="html"/>
+ <delete dir="${build.dir}"/>
<delete file="${target.jar}"/>
<delete file="${dist-version}.zip"/>
</target>
diff --git a/pom.xml b/pom.xml
index 0b8c335..eaef95f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,14 +2,22 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
- <packaging>jar</packaging>
- <description>Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation
- simple. It is a class library for editing bytecodes in Java.
+ <packaging>bundle</packaging>
+ <description>
+ Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation
+ simple. It is a class library for editing bytecodes in Java.
</description>
- <version>3.14.0-GA</version>
+ <version>3.24.1-GA</version>
<name>Javassist</name>
<url>http://www.javassist.org/</url>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <organization>
+ <name>Shigeru Chiba, www.javassist.org</name>
+ </organization>
+
<issueManagement>
<system>JIRA</system>
<url>https://jira.jboss.org/jira/browse/JASSIST/</url>
@@ -28,25 +36,31 @@
<name>LGPL 2.1</name>
<url>http://www.gnu.org/licenses/lgpl-2.1.html</url>
</license>
+ <!-- this is the license under which javassist can be distributed.
+ -->
+ <license>
+ <name>Apache License 2.0</name>
+ <url>http://www.apache.org/licenses/</url>
+ </license>
</licenses>
<scm>
- <connection>scm:svn:http://anonsvn.jboss.org/repos/javassist/</connection>
- <developerConnection>scm:svn:https://svn.jboss.org/repos/javassist/</developerConnection>
- <url>http://fisheye.jboss.org/browse/javassist/</url>
+ <connection>scm:git:git@github.com:jboss-javassist/javassist.git</connection>
+ <developerConnection>scm:git:git@github.com:jboss-javassist/javassist.git</developerConnection>
+ <url>scm:git:git@github.com:jboss-javassist/javassist.git</url>
</scm>
<developers>
<developer>
<id>chiba</id>
<name>Shigeru Chiba</name>
- <email>chiba@acm.org</email>
- <organization>Tokyo Institute of Technology</organization>
+ <email>chiba@javassist.org</email>
+ <organization>The Javassist Project</organization>
<organizationUrl>http://www.javassist.org/</organizationUrl>
<roles>
<role>project lead</role>
</roles>
- <timezone>8</timezone>
+ <timezone>9</timezone>
</developer>
<developer>
@@ -72,6 +86,20 @@
</roles>
<timezone>0</timezone>
</developer>
+
+ <developer>
+ <id>scottmarlow</id>
+ <name>Scott Marlow</name>
+ <email>smarlow@redhat.com</email>
+ <organization>JBoss</organization>
+ <organizationUrl>http://www.jboss.org/</organizationUrl>
+ <roles>
+ <role>contributing developer</role>
+ </roles>
+ <timezone>-5</timezone>
+ </developer>
+
+
</developers>
<distributionManagement>
@@ -112,27 +140,55 @@
<build>
<sourceDirectory>src/main/</sourceDirectory>
<testSourceDirectory>src/test/</testSourceDirectory>
+ <testResources>
+ <testResource>
+ <directory>src/test/resources</directory>
+ </testResource>
+ </testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
+ <version>3.2</version>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ <testSource>11</testSource>
+ <testTarget>11</testTarget>
+ <testCompilerArgument>-parameters</testCompilerArgument>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.18.1</version>
<configuration>
- <source>1.4</source>
- <target>1.4</target>
+ <includes>
+ <include>javassist/JvstTest.java</include>
+ </includes>
+ <forkMode>once</forkMode>
+ <additionalClasspathElements>
+ <additionalClasspathElement>resources</additionalClasspathElement>
+ </additionalClasspathElements>
+ <workingDirectory>${project.build.directory}/runtest</workingDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
+ <version>2.6</version>
<configuration>
<archive>
- <manifestFile>${project.build.sourceDirectory}/META-INF/MANIFEST.MF</manifestFile>
+ <manifest>
+ <mainClass>javassist.CtClass</mainClass>
+ <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+ </manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
- <version>2.0.3</version>
+ <version>2.0.4</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -146,11 +202,45 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
- <version>2.7</version>
+ <version>3.0.1</version>
<configuration>
<attach>true</attach>
+ <excludePackageNames>javassist.compiler:javassist.convert:javassist.scopedpool:javassist.bytecode.stackmap</excludePackageNames>
+ <bottom><![CDATA[<i>Javassist, a Java-bytecode translator toolkit.<br>
+Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.</i>]]></bottom>
+ <show>public</show>
+ <nohelp>true</nohelp>
+ <doclint>none</doclint>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>3.3.0</version>
+ <executions>
+ <execution>
+ <id>bundle-manifest</id>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>manifest</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <supportedProjectTypes>
+ <supportedProjectType>jar</supportedProjectType>
+ <supportedProjectType>bundle</supportedProjectType>
+ <supportedProjectType>war</supportedProjectType>
+ </supportedProjectTypes>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Version>${project.version}</Bundle-Version>
+ <Import-Package>!com.sun.jdi.*</Import-Package>
+ <Export-Package>!com.sun.jdi.*,javassist.*;version="${project.version}"</Export-Package>
+ </instructions>
+ </configuration>
+ <extensions>true</extensions>
+ </plugin>
</plugins>
</build>
<profiles>
@@ -194,37 +284,15 @@
needed by sample code
-->
<profile>
- <id>jdk14</id>
- <activation>
- <jdk>1.4</jdk>
- <property>
- <name>!no.tools</name>
- </property>
- </activation>
- <dependencies>
- <dependency>
- <groupId>com.sun</groupId>
- <artifactId>tools</artifactId>
- <version>1.4</version>
- <scope>system</scope>
- <optional>true</optional>
- <systemPath>${java.home}/../lib/tools.jar</systemPath>
- </dependency>
- </dependencies>
- </profile>
- <profile>
- <id>jdk15</id>
+ <id>default-tools</id>
<activation>
- <jdk>1.5</jdk>
- <property>
- <name>!no.tools</name>
- </property>
+ <jdk>[,1.8]</jdk>
</activation>
<dependencies>
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
- <version>1.5</version>
+ <version>${java.version}</version>
<scope>system</scope>
<optional>true</optional>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
@@ -232,31 +300,35 @@
</dependencies>
</profile>
<profile>
- <id>jdk16</id>
+ <id>java9-tools</id>
<activation>
- <jdk>1.6</jdk>
- <property>
- <name>!no.tools</name>
- </property>
+ <jdk>[1.9,]</jdk>
</activation>
<dependencies>
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
- <version>1.6</version>
+ <version>${java.version}</version>
<scope>system</scope>
<optional>true</optional>
- <systemPath>${java.home}/../lib/tools.jar</systemPath>
+ <systemPath>${java.home}/lib/jrt-fs.jar</systemPath>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.1</version>
- <scope>test</scope>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <version>1.3</version>
+ <scope>test</scope>
</dependency>
</dependencies>
</project>
+
diff --git a/regenerate_from_source.sh b/regenerate_from_source.sh
deleted file mode 100644
index 2f744e1..0000000
--- a/regenerate_from_source.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2011 The Android Open Source Project.
-# This script imports the src, test, etc. from javassist. It DOESN'T commit
-# to git giving you a chance to review the changes. Remember that changes in
-# bin are normally ignored by git, but we need to force them this case.
-#
-# This script doesn't take any parameter.
-
-svn export --force http://anonsvn.jboss.org/repos/javassist/trunk .
-rm lib/junit.jar
-# I don't check if there is something on lib on purpose; that way, if
-# anything new is added, we will get a visible error.
-rmdir lib
diff --git a/sample/preproc/Assistant.java b/sample/preproc/Assistant.java
index 81b93b2..7ef15c4 100644
--- a/sample/preproc/Assistant.java
+++ b/sample/preproc/Assistant.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/sample/preproc/Compiler.java b/sample/preproc/Compiler.java
index 5481c0f..085e09e 100644
--- a/sample/preproc/Compiler.java
+++ b/sample/preproc/Compiler.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/META-INF/MANIFEST.MF b/src/main/META-INF/MANIFEST.MF
index 02ac494..72421eb 100644
--- a/src/main/META-INF/MANIFEST.MF
+++ b/src/main/META-INF/MANIFEST.MF
@@ -1,8 +1,5 @@
-Manifest-Version: 1.1
Specification-Title: Javassist
-Created-By: Shigeru Chiba, Tokyo Institute of Technology
-Specification-Vendor: Shigeru Chiba, Tokyo Institute of Technology
-Specification-Version: 3.14.0.GA
+Specification-Vendor: Shigeru Chiba, www.javassist.org
+Specification-Version: 3.24.1-GA
Main-Class: javassist.CtClass
-
-Name: javassist/
+Automatic-Module-Name: org.javassist
diff --git a/src/main/javassist/ByteArrayClassPath.java b/src/main/javassist/ByteArrayClassPath.java
index f09f81f..95ea418 100644
--- a/src/main/javassist/ByteArrayClassPath.java
+++ b/src/main/javassist/ByteArrayClassPath.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,13 @@
package javassist;
-import java.io.*;
-import java.net.URL;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
/**
* A <code>ByteArrayClassPath</code> contains bytes that is served as
@@ -28,15 +33,15 @@ import java.net.MalformedURLException;
* into a <code>CtClass</code> object representing the class with a name
* <code>classname</code>, then do as following:
*
- * <ul><pre>
+ * <pre>
* ClassPool cp = ClassPool.getDefault();
* cp.insertClassPath(new ByteArrayClassPath(classname, b));
* CtClass cc = cp.get(classname);
- * </pre></ul>
+ * </pre>
*
* <p>The <code>ClassPool</code> object <code>cp</code> uses the created
* <code>ByteArrayClassPath</code> object as the source of the class file.
- *
+ *
* <p>A <code>ByteArrayClassPath</code> must be instantiated for every
* class. It contains only a single class file.
*
@@ -61,11 +66,7 @@ public class ByteArrayClassPath implements ClassPath {
this.classfile = classfile;
}
- /**
- * Closes this class path.
- */
- public void close() {}
-
+ @Override
public String toString() {
return "byte[]:" + classname;
}
@@ -73,26 +74,49 @@ public class ByteArrayClassPath implements ClassPath {
/**
* Opens the class file.
*/
+ @Override
public InputStream openClassfile(String classname) {
if(this.classname.equals(classname))
return new ByteArrayInputStream(classfile);
- else
- return null;
+ return null;
}
/**
* Obtains the URL.
*/
+ @Override
public URL find(String classname) {
if(this.classname.equals(classname)) {
String cname = classname.replace('.', '/') + ".class";
try {
- // return new File(cname).toURL();
- return new URL("file:/ByteArrayClassPath/" + cname);
+ return new URL(null, "file:/ByteArrayClassPath/" + cname, new BytecodeURLStreamHandler());
}
catch (MalformedURLException e) {}
}
return null;
}
+
+ private class BytecodeURLStreamHandler extends URLStreamHandler {
+ protected URLConnection openConnection(final URL u) {
+ return new BytecodeURLConnection(u);
+ }
+ }
+
+ private class BytecodeURLConnection extends URLConnection {
+ protected BytecodeURLConnection(URL url) {
+ super(url);
+ }
+
+ public void connect() throws IOException {
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return new ByteArrayInputStream(classfile);
+ }
+
+ public int getContentLength() {
+ return classfile.length;
+ }
+ }
}
diff --git a/src/main/javassist/CannotCompileException.java b/src/main/javassist/CannotCompileException.java
index 35ad6e7..d8a6e19 100644
--- a/src/main/javassist/CannotCompileException.java
+++ b/src/main/javassist/CannotCompileException.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -21,13 +22,16 @@ import javassist.compiler.CompileError;
* Thrown when bytecode transformation has failed.
*/
public class CannotCompileException extends Exception {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private Throwable myCause;
/**
* Gets the cause of this throwable.
* It is for JDK 1.3 compatibility.
*/
- public Throwable getCause() {
+ @Override
+ public synchronized Throwable getCause() {
return (myCause == this ? null : myCause);
}
@@ -35,6 +39,7 @@ public class CannotCompileException extends Exception {
* Initializes the cause of this throwable.
* It is for JDK 1.3 compatibility.
*/
+ @Override
public synchronized Throwable initCause(Throwable cause) {
myCause = cause;
return this;
@@ -48,8 +53,7 @@ public class CannotCompileException extends Exception {
public String getReason() {
if (message != null)
return message;
- else
- return this.toString();
+ return this.toString();
}
/**
diff --git a/src/main/javassist/ClassClassPath.java b/src/main/javassist/ClassClassPath.java
index c9925d8..0c2201f 100644
--- a/src/main/javassist/ClassClassPath.java
+++ b/src/main/javassist/ClassClassPath.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -26,10 +27,10 @@ import java.net.URL;
* with a user-defined class loader and any class files are not found with
* the default <code>ClassPool</code>. For example,
*
- * <ul><pre>
+ * <pre>
* ClassPool cp = ClassPool.getDefault();
* cp.insertClassPath(new ClassClassPath(this.getClass()));
- * </pre></ul>
+ * </pre>
*
* This code snippet permanently adds a <code>ClassClassPath</code>
* to the default <code>ClassPool</code>. Note that the default
@@ -37,12 +38,16 @@ import java.net.URL;
* <code>ClassClassPath</code> uses a class object representing
* the class including the code snippet above.
*
+ * <p>Class files in a named module are private to that module.
+ * This method cannot obtain class files in named modules.
+ * </p>
+ *
* @see ClassPool#insertClassPath(ClassPath)
* @see ClassPool#appendClassPath(ClassPath)
* @see LoaderClassPath
*/
public class ClassClassPath implements ClassPath {
- private Class thisClass;
+ private Class<?> thisClass;
/** Creates a search path.
*
@@ -50,7 +55,7 @@ public class ClassClassPath implements ClassPath {
* file. <code>getResourceAsStream()</code> is called on
* this object.
*/
- public ClassClassPath(Class c) {
+ public ClassClassPath(Class<?> c) {
thisClass = c;
}
@@ -69,27 +74,24 @@ public class ClassClassPath implements ClassPath {
/**
* Obtains a class file by <code>getResourceAsStream()</code>.
*/
- public InputStream openClassfile(String classname) {
- String jarname = "/" + classname.replace('.', '/') + ".class";
- return thisClass.getResourceAsStream(jarname);
+ @Override
+ public InputStream openClassfile(String classname) throws NotFoundException {
+ String filename = '/' + classname.replace('.', '/') + ".class";
+ return thisClass.getResourceAsStream(filename);
}
/**
* Obtains the URL of the specified class file.
*
- * @return null if the class file could not be found.
+ * @return null if the class file could not be found.
*/
+ @Override
public URL find(String classname) {
- String jarname = "/" + classname.replace('.', '/') + ".class";
- return thisClass.getResource(jarname);
- }
-
- /**
- * Does nothing.
- */
- public void close() {
+ String filename = '/' + classname.replace('.', '/') + ".class";
+ return thisClass.getResource(filename);
}
+ @Override
public String toString() {
return thisClass.getName() + ".class";
}
diff --git a/src/main/javassist/ClassMap.java b/src/main/javassist/ClassMap.java
index e923a38..0e23e5f 100644
--- a/src/main/javassist/ClassMap.java
+++ b/src/main/javassist/ClassMap.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,6 +16,8 @@
package javassist;
+import java.util.HashMap;
+
import javassist.bytecode.Descriptor;
/**
@@ -24,7 +27,7 @@ import javassist.bytecode.Descriptor;
* definition or a method body. Define a subclass of this class
* if a more complex mapping algorithm is needed. For example,
*
- * <ul><pre>class MyClassMap extends ClassMap {
+ * <pre>class MyClassMap extends ClassMap {
* public Object get(Object jvmClassName) {
* String name = toJavaName((String)jvmClassName);
* if (name.startsWith("java."))
@@ -32,7 +35,7 @@ import javassist.bytecode.Descriptor;
* else
* return super.get(jvmClassName);
* }
- * }</pre></ul>
+ * }</pre>
*
* <p>This subclass maps <code>java.lang.String</code> to
* <code>java2.lang.String</code>. Note that <code>get()</code>
@@ -40,11 +43,15 @@ import javassist.bytecode.Descriptor;
* For example, the internal representation of <code>java.lang.String</code>
* is <code>java/lang/String</code>.
*
+ * <p>Note that this is a map from <code>String</code> to <code>String</code>.
+ *
* @see #get(Object)
* @see CtClass#replaceClassName(ClassMap)
* @see CtNewMethod#copy(CtMethod,String,CtClass,ClassMap)
*/
-public class ClassMap extends java.util.HashMap {
+public class ClassMap extends HashMap<String,String> {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private ClassMap parent;
/**
@@ -71,7 +78,7 @@ public class ClassMap extends java.util.HashMap {
/**
* Maps a class name to another name in this hashtable.
* If the hashtable contains another mapping from the same
- * class name, the old mapping is replaced.
+ * class name, the old mapping is replaced.
* This method translates the given class names into the
* internal form used in the JVM before putting it in
* the hashtable.
@@ -86,21 +93,23 @@ public class ClassMap extends java.util.HashMap {
* @param newname the substituted class name.
* @see #fix(String)
*/
- public void put(String oldname, String newname) {
+ @Override
+ public String put(String oldname, String newname) {
if (oldname == newname)
- return;
+ return oldname;
String oldname2 = toJvmName(oldname);
- String s = (String)get(oldname2);
+ String s = get(oldname2);
if (s == null || !s.equals(oldname2))
- super.put(oldname2, toJvmName(newname));
+ return super.put(oldname2, toJvmName(newname));
+ return s;
}
/**
* Is equivalent to <code>put()</code> except that
* the given mapping is not recorded into the hashtable
* if another mapping from <code>oldname</code> is
- * already included.
+ * already included.
*
* @param oldname the original class name.
* @param newname the substituted class name.
@@ -110,13 +119,13 @@ public class ClassMap extends java.util.HashMap {
return;
String oldname2 = toJvmName(oldname);
- String s = (String)get(oldname2);
+ String s = get(oldname2);
if (s == null)
super.put(oldname2, toJvmName(newname));
}
- protected final void put0(Object oldname, Object newname) {
- super.put(oldname, newname);
+ protected final String put0(String oldname, String newname) {
+ return super.put(oldname, newname);
}
/**
@@ -129,14 +138,13 @@ public class ClassMap extends java.util.HashMap {
* @see #toJvmName(String)
* @see #toJavaName(String)
*/
- public Object get(Object jvmClassName) {
- Object found = super.get(jvmClassName);
+ @Override
+ public String get(Object jvmClassName) {
+ String found = super.get(jvmClassName);
if (found == null && parent != null)
return parent.get(jvmClassName);
- else
- return found;
+ return found;
}
-
/**
* Prevents a mapping from the specified class name to another name.
*/
diff --git a/src/main/javassist/ClassPath.java b/src/main/javassist/ClassPath.java
index d34b279..5f10edc 100644
--- a/src/main/javassist/ClassPath.java
+++ b/src/main/javassist/ClassPath.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -38,7 +39,7 @@ public interface ClassPath {
*
* <p>This method can return null if the specified class file is not
* found. If null is returned, the next search path is examined.
- * However, if an error happens, this method must throw an exception
+ * However, if an error happens, this method must throw an exception
* so that the search will be terminated.
*
* <p>This method should not modify the contents of the class file.
@@ -57,11 +58,4 @@ public interface ClassPath {
* @return null if the specified class file could not be found.
*/
URL find(String classname);
-
- /**
- * This method is invoked when the <code>ClassPath</code> object is
- * detached from the search path. It will be an empty method in most of
- * classes.
- */
- void close();
}
diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java
index b75bfb6..37f2acb 100644
--- a/src/main/javassist/ClassPool.java
+++ b/src/main/javassist/ClassPool.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -20,17 +21,16 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.lang.reflect.Method;
import java.net.URL;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
-import java.util.Hashtable;
-import java.util.Iterator;
import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import javassist.bytecode.ClassFile;
import javassist.bytecode.Descriptor;
+import javassist.util.proxy.DefinePackageHelper;
/**
* A container of <code>CtClass</code> objects.
@@ -66,30 +66,8 @@ import javassist.bytecode.Descriptor;
* @see javassist.CtClass
* @see javassist.ClassPath
*/
+@SuppressWarnings({"unchecked", "rawtypes"})
public class ClassPool {
- // used by toClass().
- private static java.lang.reflect.Method defineClass1, defineClass2;
-
- static {
- try {
- AccessController.doPrivileged(new PrivilegedExceptionAction(){
- public Object run() throws Exception{
- Class cl = Class.forName("java.lang.ClassLoader");
- defineClass1 = cl.getDeclaredMethod("defineClass",
- new Class[] { String.class, byte[].class,
- int.class, int.class });
-
- defineClass2 = cl.getDeclaredMethod("defineClass",
- new Class[] { String.class, byte[].class,
- int.class, int.class, ProtectionDomain.class });
- return null;
- }
- });
- }
- catch (PrivilegedActionException pae) {
- throw new RuntimeException("cannot initialize ClassPool", pae.getException());
- }
- }
/**
* Determines the search order.
@@ -206,12 +184,13 @@ public class ClassPool {
* <p>When this method is called for the first time, the default
* class pool is created with the following code snippet:
*
- * <ul><code>ClassPool cp = new ClassPool();
+ * <pre>ClassPool cp = new ClassPool();
* cp.appendSystemPath();
- * </code></ul>
+ * </pre>
*
* <p>If the default class pool cannot find any class files,
- * try <code>ClassClassPath</code> and <code>LoaderClassPath</code>.
+ * try <code>ClassClassPath</code>, <code>ModuleClassPath</code>,
+ * or <code>LoaderClassPath</code>.
*
* @see ClassClassPath
* @see LoaderClassPath
@@ -243,7 +222,7 @@ public class ClassPool {
* caching of classes.
*
* @see #getCached(String)
- * @see #removeCached(String,CtClass)
+ * @see #removeCached(String)
*/
protected void cacheCtClass(String classname, CtClass c, boolean dynamic) {
classes.put(classname, c);
@@ -319,22 +298,26 @@ public class ClassPool {
* @see #importPackage(String)
* @since 3.1
*/
- public Iterator getImportedPackages() {
+ public Iterator<String> getImportedPackages() {
return importedPackages.iterator();
}
/**
- * Records a name that never exists.
+ * Records a class name that never exists.
* For example, a package name can be recorded by this method.
* This would improve execution performance
- * since <code>get()</code> does not search the class path at all
+ * since <code>get()</code> quickly throw an exception
+ * without searching the class path at all
* if the given name is an invalid name recorded by this method.
* Note that searching the class path takes relatively long time.
*
- * @param name a class name (separeted by dot).
+ * <p>The current implementation of this method performs nothing.
+ *
+ * @param name an invalid class name (separeted by dots).
+ * @deprecated
*/
public void recordInvalidClassName(String name) {
- source.recordInvalidClassName(name);
+ // source.recordInvalidClassName(name);
}
/**
@@ -370,9 +353,9 @@ public class ClassPool {
* This method is useful if you want to generate a new class as a copy
* of another class (except the class name). For example,
*
- * <ul><pre>
+ * <pre>
* getAndRename("Point", "Pair")
- * </pre></ul>
+ * </pre>
*
* returns a <code>CtClass</code> object representing <code>Pair</code>
* class. The definition of <code>Pair</code> is the same as that of
@@ -506,7 +489,6 @@ public class ClassPool {
/**
* @param useCache false if the cached CtClass must be ignored.
- * @param searchParent false if the parent class pool is not searched.
* @return null if the class could not be found.
*/
protected synchronized CtClass get0(String classname, boolean useCache)
@@ -726,6 +708,54 @@ public class ClassPool {
/**
* Creates a new class (or interface) from the given class file.
+ * If there already exists a class with the same name, the new class
+ * overwrites that previous class.
+ *
+ * <p>This method is used for creating a <code>CtClass</code> object
+ * directly from a class file. The qualified class name is obtained
+ * from the class file; you do not have to explicitly give the name.
+ *
+ * @param classfile class file.
+ * @throws RuntimeException if there is a frozen class with the
+ * the same name.
+ * @since 3.20
+ */
+ public CtClass makeClass(ClassFile classfile)
+ throws RuntimeException
+ {
+ return makeClass(classfile, true);
+ }
+
+ /**
+ * Creates a new class (or interface) from the given class file.
+ * If there already exists a class with the same name, the new class
+ * overwrites that previous class.
+ *
+ * <p>This method is used for creating a <code>CtClass</code> object
+ * directly from a class file. The qualified class name is obtained
+ * from the class file; you do not have to explicitly give the name.
+ *
+ * @param classfile class file.
+ * @param ifNotFrozen throws a RuntimeException if this parameter is true
+ * and there is a frozen class with the same name.
+ * @since 3.20
+ */
+ public CtClass makeClass(ClassFile classfile, boolean ifNotFrozen)
+ throws RuntimeException
+ {
+ compress();
+ CtClass clazz = new CtClassType(classfile, this);
+ clazz.checkModify();
+ String classname = clazz.getName();
+ if (ifNotFrozen)
+ checkNotFrozen(classname);
+
+ cacheCtClass(classname, clazz, true);
+ return clazz;
+ }
+
+ /**
+ * Creates a new class (or interface) from the given class file.
* If there already exists a class with the same name, this method
* returns the existing class; a new class is never created from
* the given class file.
@@ -804,14 +834,14 @@ public class ClassPool {
/**
* Creates a new public nested class.
- * This method is called by CtClassType.makeNestedClass().
+ * This method is called by {@link CtClassType#makeNestedClass()}.
*
* @param classname a fully-qualified class name.
* @return the nested class.
*/
synchronized CtClass makeNestedClass(String classname) {
checkNotFrozen(classname);
- CtClass clazz = new CtNewNestedClass(classname, this, false, null);
+ CtClass clazz = new CtNewClass(classname, this, false, null);
cacheCtClass(classname, clazz, true);
return clazz;
}
@@ -847,6 +877,28 @@ public class ClassPool {
}
/**
+ * Creates a new annotation.
+ * If there already exists a class/interface with the same name,
+ * the new interface overwrites that previous one.
+ *
+ * @param name a fully-qualified interface name.
+ * Or null if the annotation has no super interface.
+ * @throws RuntimeException if the existing interface is frozen.
+ * @since 3.19
+ */
+ public CtClass makeAnnotation(String name) throws RuntimeException {
+ try {
+ CtClass cc = makeInterface(name, get("java.lang.annotation.Annotation"));
+ cc.setModifiers(cc.getModifiers() | Modifier.ANNOTATION);
+ return cc;
+ }
+ catch (NotFoundException e) {
+ // should never happen.
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ /**
* Appends the system search path to the end of the
* search path. The system search path
* usually includes the platform library, extension
@@ -925,7 +977,7 @@ public class ClassPool {
/**
* Detatches the <code>ClassPath</code> object from the search path.
* The detached <code>ClassPath</code> object cannot be added
- * to the pathagain.
+ * to the path again.
*/
public void removeClassPath(ClassPath cp) {
source.removeClassPath(cp);
@@ -971,16 +1023,23 @@ public class ClassPool {
* the <code>getClassLoader()</code> method.
* If the program is running on some application
* server, the context class loader might be inappropriate to load the
- * class.
+ * class.</p>
*
* <p>This method is provided for convenience. If you need more
* complex functionality, you should write your own class loader.
*
- * <p><b>Warining:</b> A Class object returned by this method may not
+ * <p><b>Warining:</b>
+ * This method should not be used in Java 11 or later.
+ * Use {@link #toClass(CtClass,Class)}.
+ * </p>
+ *
+ * <p><b>Warining:</b>
+ * A Class object returned by this method may not
* work with a security manager or a signed jar file because a
- * protection domain is not specified.
+ * protection domain is not specified.</p>
*
- * @see #toClass(CtClass, java.lang.ClassLoader, ProtectionDomain)
+ * @see #toClass(CtClass,Class)
+ * @see #toClass(CtClass,Class,java.lang.ClassLoader,ProtectionDomain)
* @see #getClassLoader()
*/
public Class toClass(CtClass clazz) throws CannotCompileException {
@@ -1014,21 +1073,21 @@ public class ClassPool {
/**
* Converts the class to a <code>java.lang.Class</code> object.
* Do not override this method any more at a subclass because
- * <code>toClass(CtClass)</code> never calls this method.
+ * {@link #toClass(CtClass)} will never calls this method.
*
* <p><b>Warining:</b> A Class object returned by this method may not
* work with a security manager or a signed jar file because a
* protection domain is not specified.
*
- * @deprecated Replaced by {@link #toClass(CtClass,ClassLoader,ProtectionDomain)}.
+ * @deprecated Replaced by {@link #toClass(CtClass,Class,ClassLoader,ProtectionDomain)}.
* A subclass of <code>ClassPool</code> that has been
* overriding this method should be modified. It should override
- * {@link #toClass(CtClass,ClassLoader,ProtectionDomain)}.
+ * {@link #toClass(CtClass,Class,ClassLoader,ProtectionDomain)}.
*/
public Class toClass(CtClass ct, ClassLoader loader)
throws CannotCompileException
{
- return toClass(ct, loader, null);
+ return toClass(ct, null, loader, null);
}
/**
@@ -1040,7 +1099,7 @@ public class ClassPool {
* loaded by the given class loader to construct a
* <code>java.lang.Class</code> object. Since a private method
* on the class loader is invoked through the reflection API,
- * the caller must have permissions to do that.
+ * the caller must have permissions to do that.</p>
*
* <p>An easy way to obtain <code>ProtectionDomain</code> object is
* to call <code>getProtectionDomain()</code>
@@ -1048,8 +1107,9 @@ public class ClassPool {
* class belongs to.
*
* <p>This method is provided for convenience. If you need more
- * complex functionality, you should write your own class loader.
+ * complex functionality, you should write your own class loader.</p>
*
+ * @param ct the class converted into {@code java.lang.Class}.
* @param loader the class loader used to load this class.
* For example, the loader returned by
* <code>getClassLoader()</code> can be used
@@ -1060,48 +1120,151 @@ public class ClassPool {
*
* @see #getClassLoader()
* @since 3.3
+ * @deprecated Replaced by {@link #toClass(CtClass,Class,ClassLoader,ProtectionDomain)}.
*/
public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain)
throws CannotCompileException
{
- try {
- byte[] b = ct.toBytecode();
- java.lang.reflect.Method method;
- Object[] args;
- if (domain == null) {
- method = defineClass1;
- args = new Object[] { ct.getName(), b, new Integer(0),
- new Integer(b.length)};
- }
- else {
- method = defineClass2;
- args = new Object[] { ct.getName(), b, new Integer(0),
- new Integer(b.length), domain};
- }
+ return toClass(ct, null, loader, domain);
+ }
- return toClass2(method, loader, args);
+ /**
+ * Converts the class to a <code>java.lang.Class</code> object.
+ * Once this method is called, further modifications are not allowed
+ * any more.
+ *
+ * <p>This method is available in Java 9 or later.
+ * It loads the class
+ * by using {@code java.lang.invoke.MethodHandles} with {@code neighbor}.
+ * </p>
+ *
+ * @param ct the class converted into {@code java.lang.Class}.
+ * @param neighbor a class belonging to the same package that
+ * the converted class belongs to.
+ * @since 3.24
+ */
+ public Class<?> toClass(CtClass ct, Class<?> neighbor)
+ throws CannotCompileException
+ {
+ try {
+ return javassist.util.proxy.DefineClassHelper.toClass(neighbor,
+ ct.toBytecode());
}
- catch (RuntimeException e) {
- throw e;
+ catch (IOException e) {
+ throw new CannotCompileException(e);
}
- catch (java.lang.reflect.InvocationTargetException e) {
- throw new CannotCompileException(e.getTargetException());
+ }
+
+ /**
+ * Converts the class to a <code>java.lang.Class</code> object.
+ * Once this method is called, further modifications are not allowed
+ * any more.
+ *
+ * <p>This method is available in Java 9 or later.
+ * It loads the class
+ * by using the given {@code java.lang.invoke.MethodHandles.Lookup}.
+ * </p>
+ *
+ * @param ct the class converted into {@code java.lang.Class}.
+ * @since 3.24
+ */
+ public Class<?> toClass(CtClass ct,
+ java.lang.invoke.MethodHandles.Lookup lookup)
+ throws CannotCompileException
+ {
+ try {
+ return javassist.util.proxy.DefineClassHelper.toClass(lookup,
+ ct.toBytecode());
}
- catch (Exception e) {
+ catch (IOException e) {
throw new CannotCompileException(e);
}
}
- private static synchronized Class toClass2(Method method,
- ClassLoader loader, Object[] args)
- throws Exception
+ /**
+ * Converts the class to a <code>java.lang.Class</code> object.
+ * Once this method is called, further modifications are not allowed
+ * any more.
+ *
+ * <p>When the JVM is Java 11 or later, this method loads the class
+ * by using {@code java.lang.invoke.MethodHandles} with {@code neighbor}.
+ * The other arguments {@code loader} and {@code domain} are not used;
+ * so they can be null.
+ * </p>
+ *
+ * <p>Otherwise, or when {@code neighbor} is null,
+ * the class file represented by the given <code>CtClass</code> is
+ * loaded by the given class loader to construct a
+ * <code>java.lang.Class</code> object. Since a private method
+ * on the class loader is invoked through the reflection API,
+ * the caller must have permissions to do that.
+ *
+ * <p>An easy way to obtain <code>ProtectionDomain</code> object is
+ * to call <code>getProtectionDomain()</code>
+ * in <code>java.lang.Class</code>. It returns the domain that the
+ * class belongs to.
+ *
+ * <p>If your program is for only Java 9 or later, don't use this method.
+ * Use {@link #toClass(CtClass,Class)} or
+ * {@link #toClass(CtClass,java.lang.invoke.MethodHandles.Lookup)}.
+ * </p>
+ *
+ * @param ct the class converted into {@code java.lang.Class}.
+ * @param neighbor a class belonging to the same package that
+ * the converted class belongs to.
+ * It can be null.
+ * @param loader the class loader used to load this class.
+ * For example, the loader returned by
+ * <code>getClassLoader()</code> can be used
+ * for this parameter.
+ * @param domain the protection domain for the class.
+ * If it is null, the default domain created
+ * by <code>java.lang.ClassLoader</code> is used.
+ *
+ * @see #getClassLoader()
+ * @since 3.24
+ */
+ public Class toClass(CtClass ct, Class<?> neighbor, ClassLoader loader,
+ ProtectionDomain domain)
+ throws CannotCompileException
{
- method.setAccessible(true);
try {
- return (Class)method.invoke(loader, args);
+ return javassist.util.proxy.DefineClassHelper.toClass(ct.getName(),
+ neighbor, loader, domain, ct.toBytecode());
}
- finally {
- method.setAccessible(false);
+ catch (IOException e) {
+ throw new CannotCompileException(e);
}
}
+
+ /**
+ * Defines a new package. If the package is already defined, this method
+ * performs nothing.
+ *
+ * <p>You do not necessarily need to
+ * call this method. If this method is called, then
+ * <code>getPackage()</code> on the <code>Class</code> object returned
+ * by <code>toClass()</code> will return a non-null object.</p>
+ *
+ * <p>The jigsaw module introduced by Java 9 has broken this method.
+ * In Java 9 or later, the VM argument
+ * <code>--add-opens java.base/java.lang=ALL-UNNAMED</code>
+ * has to be given to the JVM so that this method can run.
+ * </p>
+ *
+ * @param loader the class loader passed to <code>toClass()</code> or
+ * the default one obtained by <code>getClassLoader()</code>.
+ * @param name the package name.
+ * @see #getClassLoader()
+ * @see #toClass(CtClass)
+ * @see CtClass#toClass()
+ * @since 3.16
+ * @deprecated
+ */
+ public void makePackage(ClassLoader loader, String name)
+ throws CannotCompileException
+ {
+ DefinePackageHelper.definePackage(name, loader);
+ }
+
}
diff --git a/src/main/javassist/ClassPoolTail.java b/src/main/javassist/ClassPoolTail.java
index aa1aefe..965d72f 100644
--- a/src/main/javassist/ClassPoolTail.java
+++ b/src/main/javassist/ClassPoolTail.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,20 @@
package javassist;
-import java.io.*;
-import java.util.jar.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
-import java.util.Hashtable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
final class ClassPathList {
ClassPathList next;
@@ -38,6 +48,7 @@ final class DirClassPath implements ClassPath {
directory = dirName;
}
+ @Override
public InputStream openClassfile(String classname) {
try {
char sep = File.separatorChar;
@@ -50,6 +61,7 @@ final class DirClassPath implements ClassPath {
return null;
}
+ @Override
public URL find(String classname) {
char sep = File.separatorChar;
String filename = directory + sep
@@ -65,8 +77,7 @@ final class DirClassPath implements ClassPath {
return null;
}
- public void close() {}
-
+ @Override
public String toString() {
return directory;
}
@@ -77,6 +88,7 @@ final class JarDirClassPath implements ClassPath {
JarDirClassPath(String dirName) throws NotFoundException {
File[] files = new File(dirName).listFiles(new FilenameFilter() {
+ @Override
public boolean accept(File dir, String name) {
name = name.toLowerCase();
return name.endsWith(".jar") || name.endsWith(".zip");
@@ -90,6 +102,7 @@ final class JarDirClassPath implements ClassPath {
}
}
+ @Override
public InputStream openClassfile(String classname) throws NotFoundException {
if (jars != null)
for (int i = 0; i < jars.length; i++) {
@@ -101,6 +114,7 @@ final class JarDirClassPath implements ClassPath {
return null; // not found
}
+ @Override
public URL find(String classname) {
if (jars != null)
for (int i = 0; i < jars.length; i++) {
@@ -111,79 +125,76 @@ final class JarDirClassPath implements ClassPath {
return null; // not found
}
-
- public void close() {
- if (jars != null)
- for (int i = 0; i < jars.length; i++)
- jars[i].close();
- }
}
final class JarClassPath implements ClassPath {
- JarFile jarfile;
+ List<String> jarfileEntries;
String jarfileURL;
JarClassPath(String pathname) throws NotFoundException {
+ JarFile jarfile = null;
try {
jarfile = new JarFile(pathname);
+ jarfileEntries = new ArrayList<String>();
+ for (JarEntry je:Collections.list(jarfile.entries()))
+ if (je.getName().endsWith(".class"))
+ jarfileEntries.add(je.getName());
jarfileURL = new File(pathname).getCanonicalFile()
- .toURI().toURL().toString();
+ .toURI().toURL().toString();
return;
+ } catch (IOException e) {}
+ finally {
+ if (null != jarfile)
+ try {
+ jarfile.close();
+ } catch (IOException e) {}
}
- catch (IOException e) {}
throw new NotFoundException(pathname);
}
+ @Override
public InputStream openClassfile(String classname)
- throws NotFoundException
+ throws NotFoundException
{
- try {
- String jarname = classname.replace('.', '/') + ".class";
- JarEntry je = jarfile.getJarEntry(jarname);
- if (je != null)
- return jarfile.getInputStream(je);
- else
- return null; // not found
- }
- catch (IOException e) {}
- throw new NotFoundException("broken jar file?: "
- + jarfile.getName());
+ URL jarURL = find(classname);
+ if (null != jarURL)
+ try {
+ java.net.URLConnection con = jarURL.openConnection();
+ con.setUseCaches(false);
+ return con.getInputStream();
+ }
+ catch (IOException e) {
+ throw new NotFoundException("broken jar file?: "
+ + classname);
+ }
+ return null;
}
+ @Override
public URL find(String classname) {
String jarname = classname.replace('.', '/') + ".class";
- JarEntry je = jarfile.getJarEntry(jarname);
- if (je != null)
+ if (jarfileEntries.contains(jarname))
try {
- return new URL("jar:" + jarfileURL + "!/" + jarname);
+ return new URL(String.format("jar:%s!/%s", jarfileURL, jarname));
}
catch (MalformedURLException e) {}
-
return null; // not found
}
- public void close() {
- try {
- jarfile.close();
- jarfile = null;
- }
- catch (IOException e) {}
- }
-
+ @Override
public String toString() {
- return jarfile == null ? "<null>" : jarfile.toString();
+ return jarfileURL == null ? "<null>" : jarfileURL.toString();
}
}
final class ClassPoolTail {
protected ClassPathList pathList;
- private Hashtable packages; // should be synchronized.
public ClassPoolTail() {
pathList = null;
- packages = new Hashtable();
}
+ @Override
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("[class path: ");
@@ -230,12 +241,13 @@ final class ClassPoolTail {
else
list = list.next;
}
-
- cp.close();
}
public ClassPath appendSystemPath() {
- return appendClassPath(new ClassClassPath());
+ if (javassist.bytecode.ClassFile.MAJOR_VERSION < javassist.bytecode.ClassFile.JAVA_9)
+ return appendClassPath(new ClassClassPath());
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ return appendClassPath(new LoaderClassPath(cl));
}
public ClassPath insertClassPath(String pathname)
@@ -269,14 +281,6 @@ final class ClassPoolTail {
}
/**
- * You can record "System" so that java.lang.System can be quickly
- * found although "System" is not a package name.
- */
- public void recordInvalidClassName(String name) {
- packages.put(name, name);
- }
-
- /**
* This method does not close the output stream.
*/
void writeClassfile(String classname, OutputStream out)
@@ -324,9 +328,6 @@ final class ClassPoolTail {
InputStream openClassfile(String classname)
throws NotFoundException
{
- if (packages.get(classname) != null)
- return null; // not found
-
ClassPathList list = pathList;
InputStream ins = null;
NotFoundException error = null;
@@ -347,8 +348,7 @@ final class ClassPoolTail {
if (error != null)
throw error;
- else
- return null; // not found
+ return null; // not found
}
/**
@@ -360,9 +360,6 @@ final class ClassPoolTail {
* @return null if the class file could not be found.
*/
public URL find(String classname) {
- if (packages.get(classname) != null)
- return null;
-
ClassPathList list = pathList;
URL url = null;
while (list != null) {
@@ -420,8 +417,12 @@ final class ClassPoolTail {
throws IOException
{
int bufsize = 4096;
- for (int i = 0; i < 8; ++i) {
- byte[] buf = new byte[bufsize];
+ byte[] buf = null;
+ for (int i = 0; i < 64; ++i) {
+ if (i < 8) {
+ bufsize *= 2;
+ buf = new byte[bufsize];
+ }
int size = 0;
int len = 0;
do {
@@ -434,7 +435,6 @@ final class ClassPoolTail {
}
} while (size < bufsize);
fout.write(buf);
- bufsize *= 2;
}
throw new IOException("too much data");
diff --git a/src/main/javassist/CodeConverter.java b/src/main/javassist/CodeConverter.java
index 44881d5..6df3622 100644
--- a/src/main/javassist/CodeConverter.java
+++ b/src/main/javassist/CodeConverter.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,8 +16,21 @@
package javassist;
-import javassist.bytecode.*;
-import javassist.convert.*;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.MethodInfo;
+import javassist.convert.TransformAccessArrayField;
+import javassist.convert.TransformAfter;
+import javassist.convert.TransformBefore;
+import javassist.convert.TransformCall;
+import javassist.convert.TransformFieldAccess;
+import javassist.convert.TransformNew;
+import javassist.convert.TransformNewClass;
+import javassist.convert.TransformReadField;
+import javassist.convert.TransformWriteField;
+import javassist.convert.Transformer;
/**
* Simple translator of method bodies
@@ -28,7 +42,7 @@ import javassist.convert.*;
* <code>CtMethod.instrument()</code> as a parameter.
*
* <p>Example:
- * <ul><pre>
+ * <pre>
* ClassPool cp = ClassPool.getDefault();
* CtClass point = cp.get("Point");
* CtClass singleton = cp.get("Singleton");
@@ -36,7 +50,7 @@ import javassist.convert.*;
* CodeConverter conv = new CodeConverter();
* conv.replaceNew(point, singleton, "makePoint");
* client.instrument(conv);
- * </pre></ul>
+ * </pre>
*
* <p>This program substitutes "<code>Singleton.makePoint()</code>"
* for all occurrences of "<code>new Point()</code>"
@@ -58,22 +72,22 @@ public class CodeConverter {
* <code>Singleton</code>, respectively)
* replaces all occurrences of:
*
- * <ul><code>new Point(x, y)</code></ul>
+ * <pre>new Point(x, y)</pre>
*
* in the method body with:
*
- * <ul><code>Singleton.createPoint(x, y)</code></ul>
+ * <pre>Singleton.createPoint(x, y)</pre>
*
* <p>This enables to intercept instantiation of <code>Point</code>
* and change the samentics. For example, the following
* <code>createPoint()</code> implements the singleton pattern:
*
- * <ul><pre>public static Point createPoint(int x, int y) {
+ * <pre>public static Point createPoint(int x, int y) {
* if (aPoint == null)
* aPoint = new Point(x, y);
* return aPoint;
* }
- * </pre></ul>
+ * </pre>
*
* <p>The static method call substituted for the original <code>new</code>
* expression must be
@@ -108,11 +122,11 @@ public class CodeConverter {
* <code>Point2</code>, respectively)
* replaces all occurrences of:
*
- * <ul><code>new Point(x, y)</code></ul>
+ * <pre>new Point(x, y)</pre>
*
* in the method body with:
*
- * <ul><code>new Point2(x, y)</code></ul>
+ * <pre>new Point2(x, y)</pre>
*
* <p>Note that <code>Point2</code> must be type-compatible with <code>Point</code>.
* It must have the same set of methods, fields, and constructors as the
@@ -156,19 +170,19 @@ public class CodeConverter {
*
* <p>For example, the program below
*
- * <ul><pre>Point p = new Point();
- * int newX = p.x + 3;</pre></ul>
+ * <pre>Point p = new Point();
+ * int newX = p.x + 3;</pre>
*
* <p>can be translated into:
*
- * <ul><pre>Point p = new Point();
- * int newX = Accessor.readX(p) + 3;</pre></ul>
+ * <pre>Point p = new Point();
+ * int newX = Accessor.readX(p) + 3;</pre>
*
* <p>where
*
- * <ul><pre>public class Accessor {
+ * <pre>public class Accessor {
* public static int readX(Object target) { ... }
- * }</pre></ul>
+ * }</pre>
*
* <p>The type of the parameter of <code>readX()</code> must
* be <code>java.lang.Object</code> independently of the actual
@@ -197,19 +211,19 @@ public class CodeConverter {
*
* <p>For example, the program below
*
- * <ul><pre>Point p = new Point();
- * p.x = 3;</pre></ul>
+ * <pre>Point p = new Point();
+ * p.x = 3;</pre>
*
* <p>can be translated into:
*
- * <ul><pre>Point p = new Point();
- * Accessor.writeX(3);</pre></ul>
+ * <pre>Point p = new Point();
+ * Accessor.writeX(3);</pre>
*
* <p>where
*
- * <ul><pre>public class Accessor {
+ * <pre>public class Accessor {
* public static void writeX(Object target, int value) { ... }
- * }</pre></ul>
+ * }</pre>
*
* <p>The type of the first parameter of <code>writeX()</code> must
* be <code>java.lang.Object</code> independently of the actual
@@ -400,27 +414,27 @@ public class CodeConverter {
* method. For example, if the originally invoked method is
* <code>move()</code>:
*
- * <ul><pre>class Point {
+ * <pre>class Point {
* Point move(int x, int y) { ... }
- * }</pre></ul>
+ * }</pre>
*
* <p>Then the before method must be something like this:
*
- * <ul><pre>class Verbose {
+ * <pre>class Verbose {
* static void print(Point target, int x, int y) { ... }
- * }</pre></ul>
+ * }</pre>
*
* <p>The <code>CodeConverter</code> would translate bytecode
* equivalent to:
*
- * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
+ * <pre>Point p2 = p.move(x + y, 0);</pre>
*
* <p>into the bytecode equivalent to:
*
- * <ul><pre>int tmp1 = x + y;
+ * <pre>int tmp1 = x + y;
* int tmp2 = 0;
* Verbose.print(p, tmp1, tmp2);
- * Point p2 = p.move(tmp1, tmp2);</pre></ul>
+ * Point p2 = p.move(tmp1, tmp2);</pre>
*
* @param origMethod the method originally invoked.
* @param beforeMethod the method invoked before
@@ -447,27 +461,28 @@ public class CodeConverter {
* method. For example, if the originally invoked method is
* <code>move()</code>:
*
- * <ul><pre>class Point {
+ * <pre>class Point {
* Point move(int x, int y) { ... }
- * }</pre></ul>
+ * }</pre>
*
* <p>Then the after method must be something like this:
*
- * <ul><pre>class Verbose {
+ * <pre>class Verbose {
* static void print(Point target, int x, int y) { ... }
- * }</pre></ul>
+ * }</pre>
*
* <p>The <code>CodeConverter</code> would translate bytecode
* equivalent to:
*
- * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
+ * <pre>Point p2 = p.move(x + y, 0);</pre>
*
* <p>into the bytecode equivalent to:
*
- * <ul><pre>int tmp1 = x + y;
+ * <pre>
+ * int tmp1 = x + y;
* int tmp2 = 0;
* Point p2 = p.move(tmp1, tmp2);
- * Verbose.print(p, tmp1, tmp2);</pre></ul>
+ * Verbose.print(p, tmp1, tmp2);</pre>
*
* @param origMethod the method originally invoked.
* @param afterMethod the method invoked after
@@ -531,6 +546,14 @@ public class CodeConverter {
if (stack > 0)
codeAttr.setMaxStack(codeAttr.getMaxStack() + stack);
+
+ try {
+ minfo.rebuildStackMapIf6(clazz.getClassPool(),
+ clazz.getClassFile2());
+ }
+ catch (BadBytecode b) {
+ throw new CannotCompileException(b.getMessage(), b);
+ }
}
/**
@@ -656,6 +679,7 @@ public class CodeConverter {
* Returns "arrayReadByteOrBoolean" as the name of the static method with the signature
* (Ljava/lang/Object;I)B to replace reading from a byte[].
*/
+ @Override
public String byteOrBooleanRead()
{
return "arrayReadByteOrBoolean";
@@ -665,6 +689,7 @@ public class CodeConverter {
* Returns "arrayWriteByteOrBoolean" as the name of the static method with the signature
* (Ljava/lang/Object;IB)V to replace writing to a byte[].
*/
+ @Override
public String byteOrBooleanWrite()
{
return "arrayWriteByteOrBoolean";
@@ -674,6 +699,7 @@ public class CodeConverter {
* Returns "arrayReadChar" as the name of the static method with the signature
* (Ljava/lang/Object;I)C to replace reading from a char[].
*/
+ @Override
public String charRead()
{
return "arrayReadChar";
@@ -683,6 +709,7 @@ public class CodeConverter {
* Returns "arrayWriteChar" as the name of the static method with the signature
* (Ljava/lang/Object;IC)V to replace writing to a byte[].
*/
+ @Override
public String charWrite()
{
return "arrayWriteChar";
@@ -692,6 +719,7 @@ public class CodeConverter {
* Returns "arrayReadDouble" as the name of the static method with the signature
* (Ljava/lang/Object;I)D to replace reading from a double[].
*/
+ @Override
public String doubleRead()
{
return "arrayReadDouble";
@@ -701,6 +729,7 @@ public class CodeConverter {
* Returns "arrayWriteDouble" as the name of the static method with the signature
* (Ljava/lang/Object;ID)V to replace writing to a double[].
*/
+ @Override
public String doubleWrite()
{
return "arrayWriteDouble";
@@ -710,6 +739,7 @@ public class CodeConverter {
* Returns "arrayReadFloat" as the name of the static method with the signature
* (Ljava/lang/Object;I)F to replace reading from a float[].
*/
+ @Override
public String floatRead()
{
return "arrayReadFloat";
@@ -719,6 +749,7 @@ public class CodeConverter {
* Returns "arrayWriteFloat" as the name of the static method with the signature
* (Ljava/lang/Object;IF)V to replace writing to a float[].
*/
+ @Override
public String floatWrite()
{
return "arrayWriteFloat";
@@ -728,6 +759,7 @@ public class CodeConverter {
* Returns "arrayReadInt" as the name of the static method with the signature
* (Ljava/lang/Object;I)I to replace reading from a int[].
*/
+ @Override
public String intRead()
{
return "arrayReadInt";
@@ -737,6 +769,7 @@ public class CodeConverter {
* Returns "arrayWriteInt" as the name of the static method with the signature
* (Ljava/lang/Object;II)V to replace writing to a int[].
*/
+ @Override
public String intWrite()
{
return "arrayWriteInt";
@@ -746,6 +779,7 @@ public class CodeConverter {
* Returns "arrayReadLong" as the name of the static method with the signature
* (Ljava/lang/Object;I)J to replace reading from a long[].
*/
+ @Override
public String longRead()
{
return "arrayReadLong";
@@ -755,6 +789,7 @@ public class CodeConverter {
* Returns "arrayWriteLong" as the name of the static method with the signature
* (Ljava/lang/Object;IJ)V to replace writing to a long[].
*/
+ @Override
public String longWrite()
{
return "arrayWriteLong";
@@ -764,6 +799,7 @@ public class CodeConverter {
* Returns "arrayReadObject" as the name of the static method with the signature
* (Ljava/lang/Object;I)Ljava/lang/Object; to replace reading from a Object[] (or any subclass of object).
*/
+ @Override
public String objectRead()
{
return "arrayReadObject";
@@ -773,6 +809,7 @@ public class CodeConverter {
* Returns "arrayWriteObject" as the name of the static method with the signature
* (Ljava/lang/Object;ILjava/lang/Object;)V to replace writing to a Object[] (or any subclass of object).
*/
+ @Override
public String objectWrite()
{
return "arrayWriteObject";
@@ -782,6 +819,7 @@ public class CodeConverter {
* Returns "arrayReadShort" as the name of the static method with the signature
* (Ljava/lang/Object;I)S to replace reading from a short[].
*/
+ @Override
public String shortRead()
{
return "arrayReadShort";
@@ -791,6 +829,7 @@ public class CodeConverter {
* Returns "arrayWriteShort" as the name of the static method with the signature
* (Ljava/lang/Object;IS)V to replace writing to a short[].
*/
+ @Override
public String shortWrite()
{
return "arrayWriteShort";
diff --git a/src/main/javassist/CtArray.java b/src/main/javassist/CtArray.java
index 42fe609..1b30cb7 100644
--- a/src/main/javassist/CtArray.java
+++ b/src/main/javassist/CtArray.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -18,26 +19,34 @@ package javassist;
/**
* Array types.
*/
-final class CtArray extends CtClass {
+final class CtArray extends CtClass
+{
protected ClassPool pool;
// the name of array type ends with "[]".
- CtArray(String name, ClassPool cp) {
+ CtArray(String name, ClassPool cp)
+ {
super(name);
pool = cp;
}
- public ClassPool getClassPool() {
+ @Override
+ public ClassPool getClassPool()
+ {
return pool;
}
- public boolean isArray() {
+ @Override
+ public boolean isArray()
+ {
return true;
}
private CtClass[] interfaces = null;
- public int getModifiers() {
+ @Override
+ public int getModifiers()
+ {
int mod = Modifier.FINAL;
try {
mod |= getComponentType().getModifiers()
@@ -47,9 +56,11 @@ final class CtArray extends CtClass {
return mod;
}
- public CtClass[] getInterfaces() throws NotFoundException {
+ @Override
+ public CtClass[] getInterfaces() throws NotFoundException
+ {
if (interfaces == null) {
- Class[] intfs = Object[].class.getInterfaces();
+ Class<?>[] intfs = Object[].class.getInterfaces();
// java.lang.Cloneable and java.io.Serializable.
// If the JVM is CLDC, intfs is empty.
interfaces = new CtClass[intfs.length];
@@ -60,7 +71,9 @@ final class CtArray extends CtClass {
return interfaces;
}
- public boolean subtypeOf(CtClass clazz) throws NotFoundException {
+ @Override
+ public boolean subtypeOf(CtClass clazz) throws NotFoundException
+ {
if (super.subtypeOf(clazz))
return true;
@@ -77,16 +90,22 @@ final class CtArray extends CtClass {
&& getComponentType().subtypeOf(clazz.getComponentType());
}
- public CtClass getComponentType() throws NotFoundException {
+ @Override
+ public CtClass getComponentType() throws NotFoundException
+ {
String name = getName();
return pool.get(name.substring(0, name.length() - 2));
}
- public CtClass getSuperclass() throws NotFoundException {
+ @Override
+ public CtClass getSuperclass() throws NotFoundException
+ {
return pool.get(javaLangObject);
}
- public CtMethod[] getMethods() {
+ @Override
+ public CtMethod[] getMethods()
+ {
try {
return getSuperclass().getMethods();
}
@@ -95,13 +114,16 @@ final class CtArray extends CtClass {
}
}
+ @Override
public CtMethod getMethod(String name, String desc)
throws NotFoundException
{
return getSuperclass().getMethod(name, desc);
}
- public CtConstructor[] getConstructors() {
+ @Override
+ public CtConstructor[] getConstructors()
+ {
try {
return getSuperclass().getConstructors();
}
diff --git a/src/main/javassist/CtBehavior.java b/src/main/javassist/CtBehavior.java
index 1414d05..1b9dbf0 100644
--- a/src/main/javassist/CtBehavior.java
+++ b/src/main/javassist/CtBehavior.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,27 @@
package javassist;
-import javassist.bytecode.*;
-import javassist.compiler.Javac;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.AnnotationsAttribute;
+import javassist.bytecode.AttributeInfo;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.ExceptionsAttribute;
+import javassist.bytecode.LineNumberAttribute;
+import javassist.bytecode.LocalVariableAttribute;
+import javassist.bytecode.LocalVariableTypeAttribute;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+import javassist.bytecode.ParameterAnnotationsAttribute;
+import javassist.bytecode.SignatureAttribute;
+import javassist.bytecode.StackMap;
+import javassist.bytecode.StackMapTable;
import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
import javassist.expr.ExprEditor;
/**
@@ -25,6 +44,11 @@ import javassist.expr.ExprEditor;
* or a static constructor (class initializer).
* It is the abstract super class of
* <code>CtMethod</code> and <code>CtConstructor</code>.
+ *
+ * <p>To directly read or modify bytecode, obtain <code>MethodInfo</code>
+ * objects.
+ *
+ * @see #getMethodInfo()
*/
public abstract class CtBehavior extends CtMember {
protected MethodInfo methodInfo;
@@ -75,6 +99,7 @@ public abstract class CtBehavior extends CtMember {
}
}
+ @Override
protected void extendToString(StringBuffer buffer) {
buffer.append(' ');
buffer.append(getName());
@@ -91,8 +116,15 @@ public abstract class CtBehavior extends CtMember {
public abstract String getLongName();
/**
- * Returns the MethodInfo representing this method/constructor in the
+ * Returns the <code>MethodInfo</code> representing this method/constructor in the
* class file.
+ *
+ * <p>If you modify the bytecode through the returned
+ * <code>MethodInfo</code> object, you might have to explicitly
+ * rebuild a stack map table. Javassist does not automatically
+ * rebuild it for avoiding unnecessary rebuilding.
+ *
+ * @see javassist.bytecode.MethodInfo#rebuildStackMap(ClassPool)
*/
public MethodInfo getMethodInfo() {
declaringClass.checkModify();
@@ -100,7 +132,7 @@ public abstract class CtBehavior extends CtMember {
}
/**
- * Returns the MethodInfo representing the method/constructor in the
+ * Returns the <code>MethodInfo</code> representing the method/constructor in the
* class file (read only).
* Normal applications do not need calling this method. Use
* <code>getMethodInfo()</code>.
@@ -127,6 +159,7 @@ public abstract class CtBehavior extends CtMember {
* <code>javassist.Modifier</code>.
* @see Modifier
*/
+ @Override
public int getModifiers() {
return AccessFlag.toModifier(methodInfo.getAccessFlags());
}
@@ -140,26 +173,28 @@ public abstract class CtBehavior extends CtMember {
*
* @see Modifier
*/
+ @Override
public void setModifiers(int mod) {
declaringClass.checkModify();
methodInfo.setAccessFlags(AccessFlag.of(mod));
}
/**
- * Returns true if the class has the specified annotation class.
+ * Returns true if the class has the specified annotation type.
*
- * @param clz the annotation class.
+ * @param typeName the name of annotation type.
* @return <code>true</code> if the annotation is found,
* otherwise <code>false</code>.
- * @since 3.11
+ * @since 3.21
*/
- public boolean hasAnnotation(Class clz) {
+ @Override
+ public boolean hasAnnotation(String typeName) {
MethodInfo mi = getMethodInfo2();
AnnotationsAttribute ainfo = (AnnotationsAttribute)
mi.getAttribute(AnnotationsAttribute.invisibleTag);
AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
mi.getAttribute(AnnotationsAttribute.visibleTag);
- return CtClassType.hasAnnotationType(clz,
+ return CtClassType.hasAnnotationType(typeName,
getDeclaringClass().getClassPool(),
ainfo, ainfo2);
}
@@ -175,7 +210,8 @@ public abstract class CtBehavior extends CtMember {
* @return the annotation if found, otherwise <code>null</code>.
* @since 3.11
*/
- public Object getAnnotation(Class clz) throws ClassNotFoundException {
+ @Override
+ public Object getAnnotation(Class<?> clz) throws ClassNotFoundException {
MethodInfo mi = getMethodInfo2();
AnnotationsAttribute ainfo = (AnnotationsAttribute)
mi.getAttribute(AnnotationsAttribute.invisibleTag);
@@ -193,6 +229,7 @@ public abstract class CtBehavior extends CtMember {
* @see #getAvailableAnnotations()
* @since 3.1
*/
+ @Override
public Object[] getAnnotations() throws ClassNotFoundException {
return getAnnotations(false);
}
@@ -206,6 +243,7 @@ public abstract class CtBehavior extends CtMember {
* @see #getAnnotations()
* @since 3.3
*/
+ @Override
public Object[] getAvailableAnnotations(){
try{
return getAnnotations(true);
@@ -306,20 +344,47 @@ public abstract class CtBehavior extends CtMember {
*
* <p>Note that the returned string is not the type signature
* contained in the <code>SignatureAttirbute</code>. It is
- * a descriptor. To obtain a type signature, call the following
- * methods:
- *
- * <ul><pre>getMethodInfo().getAttribute(SignatureAttribute.tag)
- * </pre></ul>
+ * a descriptor.
*
* @see javassist.bytecode.Descriptor
- * @see javassist.bytecode.SignatureAttribute
+ * @see #getGenericSignature()
*/
+ @Override
public String getSignature() {
return methodInfo.getDescriptor();
}
/**
+ * Returns the generic signature of the method.
+ * It represents parameter types including type variables.
+ *
+ * @see SignatureAttribute#toMethodSignature(String)
+ * @since 3.17
+ */
+ @Override
+ public String getGenericSignature() {
+ SignatureAttribute sa
+ = (SignatureAttribute)methodInfo.getAttribute(SignatureAttribute.tag);
+ return sa == null ? null : sa.getSignature();
+ }
+
+ /**
+ * Set the generic signature of the method.
+ * It represents parameter types including type variables.
+ * See {@link javassist.CtClass#setGenericSignature(String)}
+ * for a code sample.
+ *
+ * @param sig a new generic signature.
+ * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode()
+ * @since 3.17
+ */
+ @Override
+ public void setGenericSignature(String sig) {
+ declaringClass.checkModify();
+ methodInfo.addAttribute(new SignatureAttribute(methodInfo.getConstPool(), sig));
+ }
+
+ /**
* Obtains exceptions that this method/constructor may throw.
*
* @return a zero-length array if there is no throws clause.
@@ -452,12 +517,13 @@ public abstract class CtBehavior extends CtMember {
*
* @param name attribute name
*/
- public byte[] getAttribute(String name) {
+ @Override
+ public byte[] getAttribute(String name)
+ {
AttributeInfo ai = methodInfo.getAttribute(name);
if (ai == null)
return null;
- else
- return ai.get();
+ return ai.get();
}
/**
@@ -470,7 +536,9 @@ public abstract class CtBehavior extends CtMember {
* @param name attribute name
* @param data attribute value
*/
- public void setAttribute(String name, byte[] data) {
+ @Override
+ public void setAttribute(String name, byte[] data)
+ {
declaringClass.checkModify();
methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(),
name, data));
@@ -493,7 +561,8 @@ public abstract class CtBehavior extends CtMember {
*
* @see javassist.runtime.Cflow
*/
- public void useCflow(String name) throws CannotCompileException {
+ public void useCflow(String name) throws CannotCompileException
+ {
CtClass cc = declaringClass;
cc.checkModify();
ClassPool pool = cc.getClassPool();
@@ -619,11 +688,15 @@ public abstract class CtBehavior extends CtMember {
ca.insertLocalVar(where, size);
LocalVariableAttribute va
- = (LocalVariableAttribute)
- ca.getAttribute(LocalVariableAttribute.tag);
+ = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.tag);
if (va != null)
va.shiftIndex(where, size);
+ LocalVariableTypeAttribute lvta
+ = (LocalVariableTypeAttribute)ca.getAttribute(LocalVariableTypeAttribute.tag);
+ if (lvta != null)
+ lvta.shiftIndex(where, size);
+
StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag);
if (smt != null)
smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo);
@@ -650,7 +723,15 @@ public abstract class CtBehavior extends CtMember {
/**
* Modifies the method/constructor body.
*
+ * <p>While executing this method, only <code>replace()</code>
+ * in <code>Expr</code> is available for bytecode modification.
+ * Other methods such as <code>insertBefore()</code> may collapse
+ * the bytecode because the <code>ExprEditor</code> loses
+ * its current position.
+ *
* @param editor specifies how to modify.
+ * @see javassist.expr.Expr#replace(String)
+ * @see #insertBefore(String)
*/
public void instrument(ExprEditor editor)
throws CannotCompileException
@@ -781,35 +862,44 @@ public abstract class CtBehavior extends CtMember {
// finally clause for exceptions
int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo,
jv, src);
- // finally clause for normal termination
- insertAfterAdvice(b, jv, src, pool, rtype, varNo);
-
- ca.setMaxStack(b.getMaxStack());
- ca.setMaxLocals(b.getMaxLocals());
-
- int gapPos = iterator.append(b.get());
- iterator.append(b.getExceptionTable(), gapPos);
-
+ int handlerPos = iterator.getCodeLength();
if (asFinally)
- ca.getExceptionTable().add(getStartPosOfBody(ca), gapPos, gapPos, 0);
-
- int gapLen = iterator.getCodeLength() - gapPos - handlerLen;
- int subr = iterator.getCodeLength() - gapLen;
+ ca.getExceptionTable().add(getStartPosOfBody(ca), handlerPos, handlerPos, 0);
+ int adviceLen = 0;
+ int advicePos = 0;
+ boolean noReturn = true;
while (iterator.hasNext()) {
int pos = iterator.next();
- if (pos >= subr)
+ if (pos >= handlerPos)
break;
int c = iterator.byteAt(pos);
if (c == Opcode.ARETURN || c == Opcode.IRETURN
|| c == Opcode.FRETURN || c == Opcode.LRETURN
|| c == Opcode.DRETURN || c == Opcode.RETURN) {
- insertGoto(iterator, subr, pos);
- subr = iterator.getCodeLength() - gapLen;
+ if (noReturn) {
+ // finally clause for normal termination
+ adviceLen = insertAfterAdvice(b, jv, src, pool, rtype, varNo);
+ handlerPos = iterator.append(b.get());
+ iterator.append(b.getExceptionTable(), handlerPos);
+ advicePos = iterator.getCodeLength() - adviceLen;
+ handlerLen = advicePos - handlerPos;
+ noReturn = false;
+ }
+ insertGoto(iterator, advicePos, pos);
+ advicePos = iterator.getCodeLength() - adviceLen;
+ handlerPos = advicePos - handlerLen;
}
}
+ if (noReturn) {
+ handlerPos = iterator.append(b.get());
+ iterator.append(b.getExceptionTable(), handlerPos);
+ }
+
+ ca.setMaxStack(b.getMaxStack());
+ ca.setMaxLocals(b.getMaxLocals());
methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
}
catch (NotFoundException e) {
@@ -823,10 +913,11 @@ public abstract class CtBehavior extends CtMember {
}
}
- private void insertAfterAdvice(Bytecode code, Javac jv, String src,
- ConstPool cp, CtClass rtype, int varNo)
+ private int insertAfterAdvice(Bytecode code, Javac jv, String src,
+ ConstPool cp, CtClass rtype, int varNo)
throws CompileError
{
+ int pc = code.currentPc();
if (rtype == CtClass.voidType) {
code.addOpcode(Opcode.ACONST_NULL);
code.addAstore(varNo);
@@ -844,6 +935,8 @@ public abstract class CtBehavior extends CtMember {
else
code.addOpcode(Opcode.ARETURN);
}
+
+ return code.currentPc() - pc;
}
/*
@@ -856,7 +949,9 @@ public abstract class CtBehavior extends CtMember {
// the gap length might be a multiple of 4.
iterator.writeByte(Opcode.NOP, pos);
boolean wide = subr + 2 - pos > Short.MAX_VALUE;
- pos = iterator.insertGapAt(pos, wide ? 4 : 2, false).position;
+ int len = wide ? 4 : 2;
+ CodeIterator.Gap gap = iterator.insertGapAt(pos, len, false);
+ pos = gap.position + gap.length - len;
int offset = iterator.getMark() - pos;
if (wide) {
iterator.writeByte(Opcode.GOTO_W, pos);
@@ -867,7 +962,11 @@ public abstract class CtBehavior extends CtMember {
iterator.write16bit(offset, pos + 1);
}
else {
- pos = iterator.insertGapAt(pos, 2, false).position;
+ if (gap.length < 4) {
+ CodeIterator.Gap gap2 = iterator.insertGapAt(gap.position, 2, false);
+ pos = gap2.position + gap2.length + gap.length - 4;
+ }
+
iterator.writeByte(Opcode.GOTO_W, pos);
iterator.write32bit(iterator.getMark() - pos, pos + 1);
}
diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java
index e805c20..5c9ca72 100644
--- a/src/main/javassist/CtClass.java
+++ b/src/main/javassist/CtClass.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -25,6 +26,7 @@ import java.io.OutputStream;
import java.net.URL;
import java.security.ProtectionDomain;
import java.util.Collection;
+
import javassist.bytecode.ClassFile;
import javassist.bytecode.Descriptor;
import javassist.bytecode.Opcode;
@@ -50,20 +52,35 @@ public abstract class CtClass {
protected String qualifiedName;
/**
+ * If the value of this field is not null, then all class
+ * files modified by Javassist are saved under the directory
+ * specified by this variable. For example, if the value is
+ * <code>"./debug"</code>, then all class files are saved
+ * there. The directory name must not end with a directory
+ * separator such as <code>/</code>.
+ *
+ * <p>The default value is null.
+ *
+ * @see #debugWriteFile(String)
+ * @since 3.16
+ */
+ public static String debugDump = null;
+
+ /**
* The version number of this release.
*/
- public static final String version = "3.14.0.GA";
+ public static final String version = "3.24.1-GA";
/**
* Prints the version number and the copyright notice.
*
* <p>The following command invokes this method:
*
- * <ul><pre>java -jar javassist.jar</pre></ul>
+ * <pre>java -jar javassist.jar</pre>
*/
public static void main(String[] args) {
System.out.println("Javassist version " + CtClass.version);
- System.out.println("Copyright (C) 1999-2010 Shigeru Chiba."
+ System.out.println("Copyright (C) 1999-2018 Shigeru Chiba."
+ " All Rights Reserved.");
}
@@ -181,6 +198,7 @@ public abstract class CtClass {
/**
* Converts the object to a string.
*/
+ @Override
public String toString() {
StringBuffer buf = new StringBuffer(getClass().getName());
buf.append("@");
@@ -344,8 +362,7 @@ public abstract class CtClass {
int index = qname.lastIndexOf('.');
if (index < 0)
return qname;
- else
- return qname.substring(index + 1);
+ return qname.substring(index + 1);
}
/**
@@ -356,8 +373,7 @@ public abstract class CtClass {
int index = qname.lastIndexOf('.');
if (index < 0)
return null;
- else
- return qname.substring(0, index);
+ return qname.substring(0, index);
}
/**
@@ -372,6 +388,94 @@ public abstract class CtClass {
}
/**
+ * Returns the generic signature of the class.
+ *
+ * <p>The generics of Java is implemented by the erasure technique.
+ * After compilation, all type parameters are dropped off from the
+ * main part of a class file. However, for reflection, the type
+ * parameters are encoded into generic signatures and attached
+ * to a class file.
+ *
+ * @return null if the generic signature is not included.
+ * @see javassist.bytecode.SignatureAttribute#toClassSignature(String)
+ * @see CtMember#getGenericSignature()
+ * @since 3.17
+ */
+ public String getGenericSignature() { return null; }
+
+ /**
+ * Sets the generic signature of the class.
+ *
+ * <p>The generics of Java is implemented by the erasure technique.
+ * After compilation, all type parameters are dropped off from the
+ * main part of a class file. However, for reflection, the type
+ * parameters must be encoded into generic signatures and attached
+ * to a class file.
+ *
+ * <p>For example,
+ *
+ * <pre>class List&lt;T&gt; {
+ * T value;
+ * T get() { return value; }
+ * void set(T v) { value = v; }
+ * }
+ * </pre>
+ *
+ * <p>this class is generated by the following code:
+ *
+ * <pre>
+ * ClassPool pool = ClassPool.getDefault();
+ * CtClass cc = pool.makeClass("List");
+ * CtClass objectClass = pool.get(CtClass.javaLangObject);
+ * ClassSignature cs = new ClassSignature(
+ * new TypeParameter[] { new TypeParameter("T") });
+ * cc.setGenericSignature(cs.encode()); // &lt;T:Ljava/lang/Object;&gt;Ljava/lang/Object;
+ *
+ * CtField f = new CtField(objClass, "value", cc);
+ * TypeVariable tvar = new TypeVariable("T");
+ * f.setGenericSignature(tvar.encode()); // TT;
+ * cc.addField(f);
+ *
+ * CtMethod m = CtNewMethod.make("public Object get(){return value;}", cc);
+ * MethodSignature ms = new MethodSignature(null, null, tvar, null);
+ * m.setGenericSignature(ms.encode()); // ()TT;
+ * cc.addMethod(m);
+ *
+ * CtMethod m2 = CtNewMethod.make("public void set(Object v){value = v;}", cc);
+ * MethodSignature ms2 = new MethodSignature(null, new Type[] { tvar },
+ * new BaseType("void"), null);
+ * m2.setGenericSignature(ms2.encode()); // (TT;)V;
+ * cc.addMethod(m2);
+ *
+ * cc.writeFile();
+ * </pre>
+ *
+ * <p>The generated class file is equivalent to the following:
+ *
+ * <pre>class List {
+ * Object value;
+ * Object get() { return value; }
+ * void set(Object v) { value = v; }
+ * }</pre>
+ *
+ * <p>but it includes generic signatures for the class, the field,
+ * and the methods so that the type variable <code>T</code> can be
+ * accessible through reflection.
+ *
+ * <p><code>MethodSignature</code> is a utility class. You can directly
+ * pass the signature string to the <code>setGenericSignature</code> method.
+ * For the specification of the signatures, see Section 4.7.9.1 <i>Signatures</i>
+ * of The Java Virtual Machine Specification (Java SE 8).
+ *
+ * @param sig a generic signature.
+ * @see javassist.bytecode.SignatureAttribute.ClassSignature#encode()
+ * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode()
+ * @see CtMember#setGenericSignature(String)
+ * @since 3.17
+ */
+ public void setGenericSignature(String sig) { checkModify(); }
+
+ /**
* Substitutes <code>newName</code> for all occurrences of a class
* name <code>oldName</code> in the class file.
*
@@ -412,27 +516,30 @@ public abstract class CtClass {
*
* @return a <code>Collection&lt;String&gt;</code> object.
*/
- public synchronized Collection getRefClasses() {
+ public synchronized Collection<String> getRefClasses() {
ClassFile cf = getClassFile2();
if (cf != null) {
ClassMap cm = new ClassMap() {
- public void put(String oldname, String newname) {
- put0(oldname, newname);
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+ @Override
+ public String put(String oldname, String newname) {
+ return put0(oldname, newname);
}
-
- public Object get(Object jvmClassName) {
+ @Override
+ public String get(Object jvmClassName) {
String n = toJavaName((String)jvmClassName);
put0(n, n);
return null;
}
+ @Override
public void fix(String name) {}
};
cf.getRefClasses(cm);
return cm.values();
}
- else
- return null;
+ return null;
}
/**
@@ -477,28 +584,39 @@ public abstract class CtClass {
}
/**
- * Returns true if the class has the specified annotation class.
+ * Returns true if the class has the specified annotation type.
*
- * @param clz the annotation class.
+ * @param annotationType the annotation type.
* @return <code>true</code> if the annotation is found, otherwise <code>false</code>.
* @since 3.11
*/
- public boolean hasAnnotation(Class clz) {
+ public boolean hasAnnotation(Class<?> annotationType) {
+ return hasAnnotation(annotationType.getName());
+ }
+
+ /**
+ * Returns true if the class has the specified annotation type.
+ *
+ * @param annotationTypeName the name of annotation type.
+ * @return <code>true</code> if the annotation is found, otherwise <code>false</code>.
+ * @since 3.21
+ */
+ public boolean hasAnnotation(String annotationTypeName) {
return false;
}
/**
- * Returns the annotation if the class has the specified annotation class.
+ * Returns the annotation if the class has the specified annotation type.
* For example, if an annotation <code>@Author</code> is associated
* with this class, an <code>Author</code> object is returned.
* The member values can be obtained by calling methods on
* the <code>Author</code> object.
*
- * @param clz the annotation class.
+ * @param clz the annotation type.
* @return the annotation if found, otherwise <code>null</code>.
* @since 3.11
*/
- public Object getAnnotation(Class clz) throws ClassNotFoundException {
+ public Object getAnnotation(Class<?> clz) throws ClassNotFoundException {
return null;
}
@@ -535,6 +653,18 @@ public abstract class CtClass {
/**
* Returns an array of nested classes declared in the class.
* Nested classes are inner classes, anonymous classes, local classes,
+ * and static nested classes. This simply calls <code>getNestedClasses()</code>.
+ *
+ * @see #getNestedClasses()
+ * @since 3.15
+ */
+ public CtClass[] getDeclaredClasses() throws NotFoundException {
+ return getNestedClasses();
+ }
+
+ /**
+ * Returns an array of nested classes declared in the class.
+ * Nested classes are inner classes, anonymous classes, local classes,
* and static nested classes.
*
* @since 3.2
@@ -650,8 +780,30 @@ public abstract class CtClass {
*
* @return null if this class is not a local class or an anonymous
* class.
+ * @deprecated The enclosing method might be a constructor.
+ * Use {@link #getEnclosingBehavior()}.
+ * @see #getEnclosingBehavior()
+ */
+ @Deprecated
+ public final CtMethod getEnclosingMethod() throws NotFoundException {
+ CtBehavior b = getEnclosingBehavior();
+ if (b == null)
+ return null;
+ else if (b instanceof CtMethod)
+ return (CtMethod)b;
+ else
+ throw new NotFoundException(b.getLongName() + " is enclosing " + getName());
+ }
+
+ /**
+ * Returns the immediately enclosing method of this class.
+ * It might be not a method but a constructor.
+ * This method works only with JDK 1.5 or later.
+ *
+ * @return null if this class is not a local class or an anonymous
+ * class.
*/
- public CtMethod getEnclosingMethod() throws NotFoundException {
+ public CtBehavior getEnclosingBehavior() throws NotFoundException {
return null;
}
@@ -860,6 +1012,20 @@ public abstract class CtClass {
}
/**
+ * Retrieves methods with the specified name among the methods
+ * declared in the class. Multiple methods with different parameters
+ * may be returned.
+ *
+ * <p>Note: this method does not search the superclasses.</p>
+ *
+ * @param name method name.
+ * @since 3.19
+ */
+ public CtMethod[] getDeclaredMethods(String name) throws NotFoundException {
+ throw new NotFoundException(name);
+ }
+
+ /**
* Retrieves the method with the specified name among the methods
* declared in the class. If there are multiple methods with
* the specified name, then this method returns one of them.
@@ -948,11 +1114,11 @@ public abstract class CtClass {
* Any regular Java expression can be used for specifying the initial
* value. The followings are examples.
*
- * <ul><pre>
+ * <pre>
* cc.addField(f, "0") // the initial value is 0.
* cc.addField(f, "i + 1") // i + 1.
* cc.addField(f, "new Point()"); // a Point object.
- * </pre></ul>
+ * </pre>
*
* <p>Here, the type of variable <code>cc</code> is <code>CtClass</code>.
* The type of <code>f</code> is <code>CtField</code>.
@@ -982,11 +1148,11 @@ public abstract class CtClass {
*
* <p>For example,
*
- * <ul><pre>
+ * <pre>
* CtClass cc = ...;
* addField(new CtField(CtClass.intType, "i", cc),
* CtField.Initializer.constant(1));
- * </pre></ul>
+ * </pre>
*
* <p>This code adds an <code>int</code> field named "i". The
* initial value of this field is 1.
@@ -1021,9 +1187,9 @@ public abstract class CtClass {
* <code>javassist.bytecode</code> package. For example, the following
* expression returns all the attributes of a class file.
*
- * <ul><pre>
+ * <pre>
* getClassFile().getAttributes()
- * </pre></ul>
+ * </pre>
*
* @param name attribute name
* @see javassist.bytecode.AttributeInfo
@@ -1046,9 +1212,9 @@ public abstract class CtClass {
* <code>javassist.bytecode</code> package. For example, the following
* expression adds an attribute <code>info</code> to a class file.
*
- * <ul><pre>
+ * <pre>
* getClassFile().addAttribute(info)
- * </pre></ul>
+ * </pre>
*
* @param name attribute name
* @param data attribute value
@@ -1095,21 +1261,86 @@ public abstract class CtClass {
* server, the context class loader might be inappropriate to load the
* class.
*
+ * <p><b>Warning:</b> In Java 11 or later, the call to this method will
+ * print a warning message:</p>
+ * <blockquote><pre>
+ * WARNING: An illegal reflective access operation has occurred
+ * WARNING: Illegal reflective access by javassist.util.proxy.SecurityActions$3 ...
+ * WARNING: Please consider reporting this to the maintainers of javassist.util.proxy.SecurityActions$3
+ * WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
+ * WARNING: All illegal access operations will be denied in a future release
+ * </pre></blockquote>
+ * <p>To avoid this message, use {@link #toClass(Class)}
+ * or {@link #toClass(java.lang.invoke.MethodHandles.Lookup)}.
+ * {@link #toClass()} will be unavailable in a future release.
+ * </p>
+ *
+ * <p><b>Warning:</b> A Class object returned by this method may not
+ * work with a security manager or a signed jar file because a
+ * protection domain is not specified.</p>
+ *
+ * <p>Note: this method calls <code>toClass()</code>
+ * in <code>ClassPool</code>.</p>
+ *
+ * @see #toClass(java.lang.invoke.MethodHandles.Lookup)
+ * @see #toClass(Class)
+ * @see ClassPool#toClass(CtClass)
+ */
+ public Class<?> toClass() throws CannotCompileException {
+ return getClassPool().toClass(this);
+ }
+
+ /**
+ * Converts this class to a <code>java.lang.Class</code> object.
+ * Once this method is called, further modifications are not
+ * allowed any more.
+ *
+ * <p>This method is provided for convenience. You should use
+ * {@code toClass(Lookup)} for better compatibility with the
+ * module system.
+ *
+ * <p>Note: this method calls <code>toClass()</code>
+ * in <code>ClassPool</code>.
+ *
+ * <p><b>Warning:</b> A Class object returned by this method may not
+ * work with a security manager or a signed jar file because a
+ * protection domain is not specified.
+ *
+ * @param neighbor A class belonging to the same package that this
+ * class belongs to. It is used to load the class.
+ * @see ClassPool#toClass(CtClass,Class)
+ * @see #toClass(java.lang.invoke.MethodHandles.Lookup)
+ * @since 3.24
+ */
+ public Class<?> toClass(Class<?> neighbor) throws CannotCompileException
+ {
+ return getClassPool().toClass(this, neighbor);
+ }
+
+ /**
+ * Converts this class to a <code>java.lang.Class</code> object.
+ * Once this method is called, further modifications are not
+ * allowed any more.
+ *
* <p>This method is provided for convenience. If you need more
* complex functionality, you should write your own class loader.
*
* <p>Note: this method calls <code>toClass()</code>
* in <code>ClassPool</code>.
*
- * <p><b>Warining:</b> A Class object returned by this method may not
+ * <p><b>Warning:</b> A Class object returned by this method may not
* work with a security manager or a signed jar file because a
* protection domain is not specified.
*
- * @see #toClass(java.lang.ClassLoader,ProtectionDomain)
- * @see ClassPool#toClass(CtClass)
+ * @param lookup used when loading the class. It has to have
+ * an access right to define a new class.
+ * @see ClassPool#toClass(CtClass,java.lang.invoke.MethodHandles.Lookup)
+ * @since 3.24
*/
- public Class toClass() throws CannotCompileException {
- return getClassPool().toClass(this);
+ public Class<?> toClass(java.lang.invoke.MethodHandles.Lookup lookup)
+ throws CannotCompileException
+ {
+ return getClassPool().toClass(this, lookup);
}
/**
@@ -1143,29 +1374,30 @@ public abstract class CtClass {
* @see ClassPool#toClass(CtClass,java.lang.ClassLoader)
* @since 3.3
*/
- public Class toClass(ClassLoader loader, ProtectionDomain domain)
+ public Class<?> toClass(ClassLoader loader, ProtectionDomain domain)
throws CannotCompileException
{
ClassPool cp = getClassPool();
if (loader == null)
loader = cp.getClassLoader();
- return cp.toClass(this, loader, domain);
+ return cp.toClass(this, null, loader, domain);
}
/**
* Converts this class to a <code>java.lang.Class</code> object.
*
- * <p><b>Warining:</b> A Class object returned by this method may not
+ * <p><b>Warning:</b> A Class object returned by this method may not
* work with a security manager or a signed jar file because a
* protection domain is not specified.
*
* @deprecated Replaced by {@link #toClass(ClassLoader,ProtectionDomain)}
*/
- public final Class toClass(ClassLoader loader)
+ @Deprecated
+ public final Class<?> toClass(ClassLoader loader)
throws CannotCompileException
{
- return getClassPool().toClass(this, loader);
+ return getClassPool().toClass(this, null, loader, null);
}
/**
@@ -1238,7 +1470,7 @@ public abstract class CtClass {
* @see ClassPool#doPruning
*
* @see #toBytecode()
- * @see #toClass()
+ * @see #toClass(Class)
* @see #writeFile()
* @see #instrument(CodeConverter)
* @see #instrument(ExprEditor)
@@ -1311,6 +1543,16 @@ public abstract class CtClass {
public void writeFile(String directoryName)
throws CannotCompileException, IOException
{
+ DataOutputStream out = makeFileOutput(directoryName);
+ try {
+ toBytecode(out);
+ }
+ finally {
+ out.close();
+ }
+ }
+
+ protected DataOutputStream makeFileOutput(String directoryName) {
String classname = getName();
String filename = directoryName + File.separatorChar
+ classname.replace('.', File.separatorChar) + ".class";
@@ -1321,15 +1563,8 @@ public abstract class CtClass {
new File(dir).mkdirs();
}
- DataOutputStream out
- = new DataOutputStream(new BufferedOutputStream(
- new DelayedFileOutputStream(filename)));
- try {
- toBytecode(out);
- }
- finally {
- out.close();
- }
+ return new DataOutputStream(new BufferedOutputStream(
+ new DelayedFileOutputStream(filename)));
}
/**
@@ -1378,27 +1613,32 @@ public abstract class CtClass {
file = new FileOutputStream(filename);
}
+ @Override
public void write(int b) throws IOException {
init();
file.write(b);
}
+ @Override
public void write(byte[] b) throws IOException {
init();
file.write(b);
}
+ @Override
public void write(byte[] b, int off, int len) throws IOException {
init();
file.write(b, off, len);
}
+ @Override
public void flush() throws IOException {
init();
file.flush();
}
+ @Override
public void close() throws IOException {
init();
file.close();
diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java
index dad5db7..ae196c4 100644
--- a/src/main/javassist/CtClassType.java
+++ b/src/main/javassist/CtClassType.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,37 +16,40 @@
package javassist;
-import java.lang.ref.WeakReference;
import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javassist.bytecode.AccessFlag;
-import javassist.bytecode.AttributeInfo;
import javassist.bytecode.AnnotationsAttribute;
+import javassist.bytecode.AttributeInfo;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.Bytecode;
import javassist.bytecode.ClassFile;
import javassist.bytecode.CodeAttribute;
-import javassist.bytecode.ConstantAttribute;
import javassist.bytecode.CodeIterator;
import javassist.bytecode.ConstPool;
+import javassist.bytecode.ConstantAttribute;
import javassist.bytecode.Descriptor;
import javassist.bytecode.EnclosingMethodAttribute;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.InnerClassesAttribute;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.ParameterAnnotationsAttribute;
+import javassist.bytecode.SignatureAttribute;
import javassist.bytecode.annotation.Annotation;
import javassist.compiler.AccessorMaker;
import javassist.compiler.CompileError;
@@ -53,7 +57,7 @@ import javassist.compiler.Javac;
import javassist.expr.ExprEditor;
/**
- * Class types.
+ * Class<?> types.
*/
class CtClassType extends CtClass {
ClassPool classPool;
@@ -64,11 +68,11 @@ class CtClassType extends CtClass {
ClassFile classfile;
byte[] rawClassfile; // backup storage
- private WeakReference memberCache;
+ private Reference<CtMember.Cache> memberCache;
private AccessorMaker accessors;
private FieldInitLink fieldInitializers;
- private Hashtable hiddenMethods; // must be synchronous
+ private Map<CtMethod,String> hiddenMethods; // must be synchronous
private int uniqueNumberSeed;
private boolean doPruning = ClassPool.doPruning;
@@ -95,6 +99,13 @@ class CtClassType extends CtClass {
qualifiedName = classfile.getName();
}
+ CtClassType(ClassFile cf, ClassPool cp) {
+ this((String)null, cp);
+ classfile = cf;
+ qualifiedName = classfile.getName();
+ }
+
+ @Override
protected void extendToString(StringBuffer buffer) {
if (wasChanged)
buffer.append("changed ");
@@ -154,6 +165,7 @@ class CtClassType extends CtClass {
}
}
+ @Override
public AccessorMaker getAccessorMaker() {
if (accessors == null)
accessors = new AccessorMaker(this);
@@ -161,19 +173,26 @@ class CtClassType extends CtClass {
return accessors;
}
+ @Override
public ClassFile getClassFile2() {
+ return getClassFile3(true);
+ }
+
+ public ClassFile getClassFile3(boolean doCompress) {
ClassFile cfile = classfile;
if (cfile != null)
return cfile;
- classPool.compress();
+ if (doCompress)
+ classPool.compress();
+
if (rawClassfile != null) {
try {
- classfile = new ClassFile(new DataInputStream(
- new ByteArrayInputStream(rawClassfile)));
+ ClassFile cf = new ClassFile(new DataInputStream(
+ new ByteArrayInputStream(rawClassfile)));
rawClassfile = null;
getCount = GET_THRESHOLD;
- return classfile;
+ return setClassFile(cf);
}
catch (IOException e) {
throw new RuntimeException(e.toString(), e);
@@ -193,8 +212,7 @@ class CtClassType extends CtClass {
+ cf.getName() + " found in "
+ qualifiedName.replace('.', '/') + ".class");
- classfile = cf;
- return cf;
+ return setClassFile(cf);
}
catch (NotFoundException e) {
throw new RuntimeException(e.toString(), e);
@@ -216,6 +234,7 @@ class CtClassType extends CtClass {
* @see javassist.CtClass#incGetCounter()
* @see #toBytecode(DataOutputStream)
*/
+ @Override
final void incGetCounter() { ++getCount; }
/**
@@ -223,6 +242,7 @@ class CtClassType extends CtClass {
* It releases the class files that have not been recently used
* if they are unmodified.
*/
+ @Override
void compress() {
if (getCount < GET_THRESHOLD)
if (!isModified() && ClassPool.releaseUnmodifiedClassFile)
@@ -238,7 +258,7 @@ class CtClassType extends CtClass {
* for saving memory space.
*/
private synchronized void saveClassFile() {
- /* getMembers() and releaseClassFile() are also synchronized.
+ /* getMembers() and removeClassFile() are also synchronized.
*/
if (classfile == null || hasMemberCache() != null)
return;
@@ -259,24 +279,39 @@ class CtClassType extends CtClass {
classfile = null;
}
+ /**
+ * Updates {@code classfile} if it is null.
+ */
+ private synchronized ClassFile setClassFile(ClassFile cf) {
+ if (classfile == null)
+ classfile = cf;
+
+ return classfile;
+ }
+
+ @Override
public ClassPool getClassPool() { return classPool; }
void setClassPool(ClassPool cp) { classPool = cp; }
+ @Override
public URL getURL() throws NotFoundException {
URL url = classPool.find(getName());
if (url == null)
throw new NotFoundException(getName());
- else
- return url;
+ return url;
}
+ @Override
public boolean isModified() { return wasChanged; }
+ @Override
public boolean isFrozen() { return wasFrozen; }
+ @Override
public void freeze() { wasFrozen = true; }
+ @Override
void checkModify() throws RuntimeException {
if (isFrozen()) {
String msg = getName() + " class is frozen";
@@ -289,11 +324,13 @@ class CtClassType extends CtClass {
wasChanged = true;
}
+ @Override
public void defrost() {
checkPruned("defrost");
wasFrozen = false;
}
+ @Override
public boolean subtypeOf(CtClass clazz) throws NotFoundException {
int i;
String cname = clazz.getName();
@@ -321,6 +358,7 @@ class CtClassType extends CtClass {
return false;
}
+ @Override
public void setName(String name) throws RuntimeException {
String oldname = getName();
if (name.equals(oldname))
@@ -335,12 +373,27 @@ class CtClassType extends CtClass {
classPool.classNameChanged(oldname, this);
}
+ @Override
+ public String getGenericSignature() {
+ SignatureAttribute sa
+ = (SignatureAttribute)getClassFile2().getAttribute(SignatureAttribute.tag);
+ return sa == null ? null : sa.getSignature();
+ }
+
+ @Override
+ public void setGenericSignature(String sig) {
+ ClassFile cf = getClassFile();
+ SignatureAttribute sa = new SignatureAttribute(cf.getConstPool(), sig);
+ cf.addAttribute(sa);
+ }
+
+ @Override
public void replaceClassName(ClassMap classnames)
throws RuntimeException
{
String oldClassName = getName();
String newClassName
- = (String)classnames.get(Descriptor.toJvmName(oldClassName));
+ = classnames.get(Descriptor.toJvmName(oldClassName));
if (newClassName != null) {
newClassName = Descriptor.toJavaName(newClassName);
// check this in advance although classNameChanged() below does.
@@ -358,6 +411,7 @@ class CtClassType extends CtClass {
}
}
+ @Override
public void replaceClassName(String oldname, String newname)
throws RuntimeException
{
@@ -371,29 +425,44 @@ class CtClassType extends CtClass {
}
}
+ @Override
public boolean isInterface() {
return Modifier.isInterface(getModifiers());
}
+ @Override
public boolean isAnnotation() {
return Modifier.isAnnotation(getModifiers());
}
+ @Override
public boolean isEnum() {
return Modifier.isEnum(getModifiers());
}
+ @Override
public int getModifiers() {
ClassFile cf = getClassFile2();
int acc = cf.getAccessFlags();
acc = AccessFlag.clear(acc, AccessFlag.SUPER);
int inner = cf.getInnerAccessFlags();
- if (inner != -1 && (inner & AccessFlag.STATIC) != 0)
- acc |= AccessFlag.STATIC;
-
+ if (inner != -1) {
+ if ((inner & AccessFlag.STATIC) != 0)
+ acc |= AccessFlag.STATIC;
+ if ((inner & AccessFlag.PUBLIC) != 0)
+ acc |= AccessFlag.PUBLIC;
+ else {
+ acc &= ~AccessFlag.PUBLIC; //clear PUBLIC
+ if ((inner & AccessFlag.PROTECTED) != 0)
+ acc |= AccessFlag.PROTECTED;
+ else if ((inner & AccessFlag.PRIVATE) != 0)
+ acc |= AccessFlag.PRIVATE;
+ }
+ }
return AccessFlag.toModifier(acc);
}
+ @Override
public CtClass[] getNestedClasses() throws NotFoundException {
ClassFile cf = getClassFile2();
InnerClassesAttribute ica
@@ -403,7 +472,7 @@ class CtClassType extends CtClass {
String thisName = cf.getName() + "$";
int n = ica.tableLength();
- ArrayList list = new ArrayList(n);
+ List<CtClass> list = new ArrayList<CtClass>(n);
for (int i = 0; i < n; i++) {
String name = ica.innerClass(i);
if (name != null)
@@ -414,34 +483,77 @@ class CtClassType extends CtClass {
}
}
- return (CtClass[])list.toArray(new CtClass[list.size()]);
+ return list.toArray(new CtClass[list.size()]);
}
+ @Override
public void setModifiers(int mod) {
+ checkModify();
+ updateInnerEntry(mod, getName(), this, true);
ClassFile cf = getClassFile2();
- if (Modifier.isStatic(mod)) {
- int flags = cf.getInnerAccessFlags();
- if (flags != -1 && (flags & AccessFlag.STATIC) != 0)
- mod = mod & ~Modifier.STATIC;
- else
- throw new RuntimeException("cannot change " + getName() + " into a static class");
+ cf.setAccessFlags(AccessFlag.of(mod & ~Modifier.STATIC));
+ }
+
+ private static void updateInnerEntry(int newMod, String name, CtClass clazz, boolean outer) {
+ ClassFile cf = clazz.getClassFile2();
+ InnerClassesAttribute ica
+ = (InnerClassesAttribute)cf.getAttribute(InnerClassesAttribute.tag);
+ if (ica != null) {
+ // If the class is a static inner class, its modifier
+ // does not contain the static bit. Its inner class attribute
+ // contains the static bit.
+ int mod = newMod & ~Modifier.STATIC;
+ int i = ica.find(name);
+ if (i >= 0) {
+ int isStatic = ica.accessFlags(i) & AccessFlag.STATIC;
+ if (isStatic != 0 || !Modifier.isStatic(newMod)) {
+ clazz.checkModify();
+ ica.setAccessFlags(i, AccessFlag.of(mod) | isStatic);
+ String outName = ica.outerClass(i);
+ if (outName != null && outer)
+ try {
+ CtClass parent = clazz.getClassPool().get(outName);
+ updateInnerEntry(mod, name, parent, false);
+ }
+ catch (NotFoundException e) {
+ throw new RuntimeException("cannot find the declaring class: "
+ + outName);
+ }
+
+ return;
+ }
+ }
}
- checkModify();
- cf.setAccessFlags(AccessFlag.of(mod));
+ if (Modifier.isStatic(newMod))
+ throw new RuntimeException("cannot change " + Descriptor.toJavaName(name)
+ + " into a static class");
}
- public boolean hasAnnotation(Class clz) {
+ @Override
+ public boolean hasAnnotation(String annotationName) {
ClassFile cf = getClassFile2();
AnnotationsAttribute ainfo = (AnnotationsAttribute)
- cf.getAttribute(AnnotationsAttribute.invisibleTag);
+ cf.getAttribute(AnnotationsAttribute.invisibleTag);
AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
- cf.getAttribute(AnnotationsAttribute.visibleTag);
- return hasAnnotationType(clz, getClassPool(), ainfo, ainfo2);
+ cf.getAttribute(AnnotationsAttribute.visibleTag);
+ return hasAnnotationType(annotationName, getClassPool(), ainfo, ainfo2);
+ }
+
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ static boolean hasAnnotationType(Class<?> clz, ClassPool cp,
+ AnnotationsAttribute a1,
+ AnnotationsAttribute a2)
+ {
+ return hasAnnotationType(clz.getName(), cp, a1, a2);
}
- static boolean hasAnnotationType(Class clz, ClassPool cp,
- AnnotationsAttribute a1, AnnotationsAttribute a2)
+ static boolean hasAnnotationType(String annotationTypeName, ClassPool cp,
+ AnnotationsAttribute a1,
+ AnnotationsAttribute a2)
{
Annotation[] anno1, anno2;
@@ -455,21 +567,21 @@ class CtClassType extends CtClass {
else
anno2 = a2.getAnnotations();
- String typeName = clz.getName();
if (anno1 != null)
- for (int i = 0; i < anno1.length; i++)
- if (anno1[i].getTypeName().equals(typeName))
- return true;
+ for (int i = 0; i < anno1.length; i++)
+ if (anno1[i].getTypeName().equals(annotationTypeName))
+ return true;
if (anno2 != null)
- for (int i = 0; i < anno2.length; i++)
- if (anno2[i].getTypeName().equals(typeName))
- return true;
+ for (int i = 0; i < anno2.length; i++)
+ if (anno2[i].getTypeName().equals(annotationTypeName))
+ return true;
return false;
}
- public Object getAnnotation(Class clz) throws ClassNotFoundException {
+ @Override
+ public Object getAnnotation(Class<?> clz) throws ClassNotFoundException {
ClassFile cf = getClassFile2();
AnnotationsAttribute ainfo = (AnnotationsAttribute)
cf.getAttribute(AnnotationsAttribute.invisibleTag);
@@ -478,7 +590,7 @@ class CtClassType extends CtClass {
return getAnnotationType(clz, getClassPool(), ainfo, ainfo2);
}
- static Object getAnnotationType(Class clz, ClassPool cp,
+ static Object getAnnotationType(Class<?> clz, ClassPool cp,
AnnotationsAttribute a1, AnnotationsAttribute a2)
throws ClassNotFoundException
{
@@ -508,10 +620,12 @@ class CtClassType extends CtClass {
return null;
}
+ @Override
public Object[] getAnnotations() throws ClassNotFoundException {
return getAnnotations(false);
}
+ @Override
public Object[] getAvailableAnnotations(){
try {
return getAnnotations(true);
@@ -567,23 +681,19 @@ class CtClassType extends CtClass {
return result;
}
- else{
- ArrayList annotations = new ArrayList();
- for (int i = 0 ; i < size1 ; i++){
- try{
- annotations.add(toAnnoType(anno1[i], cp));
- }
- catch(ClassNotFoundException e){}
- }
- for (int j = 0; j < size2; j++) {
- try{
- annotations.add(toAnnoType(anno2[j], cp));
- }
- catch(ClassNotFoundException e){}
- }
+ List<Object> annotations = new ArrayList<Object>();
+ for (int i = 0 ; i < size1 ; i++)
+ try{
+ annotations.add(toAnnoType(anno1[i], cp));
+ }
+ catch(ClassNotFoundException e){}
+ for (int j = 0; j < size2; j++)
+ try{
+ annotations.add(toAnnoType(anno2[j], cp));
+ }
+ catch(ClassNotFoundException e){}
- return annotations.toArray();
- }
+ return annotations.toArray();
}
static Object[][] toAnnotationType(boolean ignoreNotFound, ClassPool cp,
@@ -632,7 +742,7 @@ class CtClassType extends CtClass {
result[i][j + size1] = toAnnoType(anno2[j], cp);
}
else{
- ArrayList annotations = new ArrayList();
+ List<Object> annotations = new ArrayList<Object>();
for (int j = 0 ; j < size1 ; j++){
try{
annotations.add(toAnnoType(anno1[j], cp));
@@ -662,10 +772,24 @@ class CtClassType extends CtClass {
}
catch (ClassNotFoundException e) {
ClassLoader cl2 = cp.getClass().getClassLoader();
- return anno.toAnnotationType(cl2, cp);
+ try {
+ return anno.toAnnotationType(cl2, cp);
+ }
+ catch (ClassNotFoundException e2){
+ try {
+ Class<?> clazz = cp.get(anno.getTypeName()).toClass();
+ return javassist.bytecode.annotation.AnnotationImpl.make(
+ clazz.getClassLoader(),
+ clazz, cp, anno);
+ }
+ catch (Throwable e3) {
+ throw new ClassNotFoundException(anno.getTypeName());
+ }
+ }
}
}
+ @Override
public boolean subclassOf(CtClass superclass) {
if (superclass == null)
return false;
@@ -684,14 +808,15 @@ class CtClassType extends CtClass {
return false;
}
+ @Override
public CtClass getSuperclass() throws NotFoundException {
String supername = getClassFile2().getSuperclass();
if (supername == null)
return null;
- else
- return classPool.get(supername);
+ return classPool.get(supername);
}
+ @Override
public void setSuperclass(CtClass clazz) throws CannotCompileException {
checkModify();
if (isInterface())
@@ -700,6 +825,7 @@ class CtClassType extends CtClass {
getClassFile2().setSuperclass(clazz.getName());
}
+ @Override
public CtClass[] getInterfaces() throws NotFoundException {
String[] ifs = getClassFile2().getInterfaces();
int num = ifs.length;
@@ -710,6 +836,7 @@ class CtClassType extends CtClass {
return ifc;
}
+ @Override
public void setInterfaces(CtClass[] list) {
checkModify();
String[] ifs;
@@ -725,12 +852,14 @@ class CtClassType extends CtClass {
getClassFile2().setInterfaces(ifs);
}
+ @Override
public void addInterface(CtClass anInterface) {
checkModify();
if (anInterface != null)
getClassFile2().addInterface(anInterface.getName());
}
+ @Override
public CtClass getDeclaringClass() throws NotFoundException {
ClassFile cf = getClassFile2();
InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute(
@@ -745,33 +874,41 @@ class CtClassType extends CtClass {
String outName = ica.outerClass(i);
if (outName != null)
return classPool.get(outName);
- else {
- // maybe anonymous or local class.
- EnclosingMethodAttribute ema
- = (EnclosingMethodAttribute)cf.getAttribute(
- EnclosingMethodAttribute.tag);
- if (ema != null)
- return classPool.get(ema.className());
- }
+
+ // maybe anonymous or local class.
+ EnclosingMethodAttribute ema
+ = (EnclosingMethodAttribute)cf.getAttribute(
+ EnclosingMethodAttribute.tag);
+ if (ema != null)
+ return classPool.get(ema.className());
+
}
return null;
}
- public CtMethod getEnclosingMethod() throws NotFoundException {
+ @Override
+ public CtBehavior getEnclosingBehavior() throws NotFoundException
+ {
ClassFile cf = getClassFile2();
EnclosingMethodAttribute ema
= (EnclosingMethodAttribute)cf.getAttribute(
EnclosingMethodAttribute.tag);
- if (ema != null) {
- CtClass enc = classPool.get(ema.className());
- return enc.getMethod(ema.methodName(), ema.methodDescriptor());
- }
-
- return null;
+ if (ema == null)
+ return null;
+ CtClass enc = classPool.get(ema.className());
+ String name = ema.methodName();
+ if (MethodInfo.nameInit.equals(name))
+ return enc.getConstructor(ema.methodDescriptor());
+ else if(MethodInfo.nameClinit.equals(name))
+ return enc.getClassInitializer();
+ else
+ return enc.getMethod(name, ema.methodDescriptor());
}
- public CtClass makeNestedClass(String name, boolean isStatic) {
+ @Override
+ public CtClass makeNestedClass(String name, boolean isStatic)
+ {
if (!isStatic)
throw new RuntimeException(
"sorry, only nested static class is supported");
@@ -812,58 +949,46 @@ class CtClassType extends CtClass {
*/
protected CtMember.Cache hasMemberCache() {
if (memberCache != null)
- return (CtMember.Cache)memberCache.get();
- else
- return null;
+ return memberCache.get();
+ return null;
}
protected synchronized CtMember.Cache getMembers() {
CtMember.Cache cache = null;
if (memberCache == null
- || (cache = (CtMember.Cache)memberCache.get()) == null) {
+ || (cache = memberCache.get()) == null) {
cache = new CtMember.Cache(this);
makeFieldCache(cache);
makeBehaviorCache(cache);
- memberCache = new WeakReference(cache);
+ memberCache = new WeakReference<CtMember.Cache>(cache);
}
return cache;
}
private void makeFieldCache(CtMember.Cache cache) {
- List list = getClassFile2().getFields();
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- FieldInfo finfo = (FieldInfo)list.get(i);
- CtField newField = new CtField(finfo, this);
- cache.addField(newField);
- }
+ List<FieldInfo> fields = getClassFile3(false).getFields();
+ for (FieldInfo finfo:fields)
+ cache.addField(new CtField(finfo, this));
}
private void makeBehaviorCache(CtMember.Cache cache) {
- List list = getClassFile2().getMethods();
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
- if (minfo.isMethod()) {
- CtMethod newMethod = new CtMethod(minfo, this);
- cache.addMethod(newMethod);
- }
- else {
- CtConstructor newCons = new CtConstructor(minfo, this);
- cache.addConstructor(newCons);
- }
- }
+ List<MethodInfo> methods = getClassFile3(false).getMethods();
+ for (MethodInfo minfo:methods)
+ if (minfo.isMethod())
+ cache.addMethod(new CtMethod(minfo, this));
+ else
+ cache.addConstructor(new CtConstructor(minfo, this));
}
+ @Override
public CtField[] getFields() {
- ArrayList alist = new ArrayList();
+ List<CtMember> alist = new ArrayList<CtMember>();
getFields(alist, this);
- return (CtField[])alist.toArray(new CtField[alist.size()]);
+ return alist.toArray(new CtField[alist.size()]);
}
- private static void getFields(ArrayList alist, CtClass cc) {
- int i, num;
+ private static void getFields(List<CtMember> alist, CtClass cc) {
if (cc == null)
return;
@@ -874,9 +999,8 @@ class CtClassType extends CtClass {
try {
CtClass[] ifs = cc.getInterfaces();
- num = ifs.length;
- for (i = 0; i < num; ++i)
- getFields(alist, ifs[i]);
+ for (CtClass ctc : ifs)
+ getFields(alist, ctc);
}
catch (NotFoundException e) {}
@@ -890,6 +1014,7 @@ class CtClassType extends CtClass {
}
}
+ @Override
public CtField getField(String name, String desc) throws NotFoundException {
CtField f = getField2(name, desc);
return checkGetField(f, name, desc);
@@ -905,10 +1030,10 @@ class CtClassType extends CtClass {
throw new NotFoundException(msg + " in " + getName());
}
- else
- return f;
+ return f;
}
+ @Override
CtField getField2(String name, String desc) {
CtField df = getDeclaredField2(name, desc);
if (df != null)
@@ -916,9 +1041,8 @@ class CtClassType extends CtClass {
try {
CtClass[] ifs = getInterfaces();
- int num = ifs.length;
- for (int i = 0; i < num; ++i) {
- CtField f = ifs[i].getField2(name, desc);
+ for (CtClass ctc : ifs) {
+ CtField f = ctc.getField2(name, desc);
if (f != null)
return f;
}
@@ -931,6 +1055,7 @@ class CtClassType extends CtClass {
return null;
}
+ @Override
public CtField[] getDeclaredFields() {
CtMember.Cache memCache = getMembers();
CtMember field = memCache.fieldHead();
@@ -946,10 +1071,12 @@ class CtClassType extends CtClass {
return cfs;
}
+ @Override
public CtField getDeclaredField(String name) throws NotFoundException {
return getDeclaredField(name, null);
}
+ @Override
public CtField getDeclaredField(String name, String desc) throws NotFoundException {
CtField f = getDeclaredField2(name, desc);
return checkGetField(f, name, desc);
@@ -969,6 +1096,7 @@ class CtClassType extends CtClass {
return null;
}
+ @Override
public CtBehavior[] getDeclaredBehaviors() {
CtMember.Cache memCache = getMembers();
CtMember cons = memCache.consHead();
@@ -993,6 +1121,7 @@ class CtClassType extends CtClass {
return cb;
}
+ @Override
public CtConstructor[] getConstructors() {
CtMember.Cache memCache = getMembers();
CtMember cons = memCache.consHead();
@@ -1024,6 +1153,7 @@ class CtClassType extends CtClass {
&& cons.isConstructor();
}
+ @Override
public CtConstructor getConstructor(String desc)
throws NotFoundException
{
@@ -1042,6 +1172,7 @@ class CtClassType extends CtClass {
return super.getConstructor(desc);
}
+ @Override
public CtConstructor[] getDeclaredConstructors() {
CtMember.Cache memCache = getMembers();
CtMember cons = memCache.consHead();
@@ -1069,6 +1200,7 @@ class CtClassType extends CtClass {
return result;
}
+ @Override
public CtConstructor getClassInitializer() {
CtMember.Cache memCache = getMembers();
CtMember cons = memCache.consHead();
@@ -1084,18 +1216,18 @@ class CtClassType extends CtClass {
return null;
}
+ @Override
public CtMethod[] getMethods() {
- HashMap h = new HashMap();
+ Map<String,CtMember> h = new HashMap<String,CtMember>();
getMethods0(h, this);
- return (CtMethod[])h.values().toArray(new CtMethod[h.size()]);
+ return h.values().toArray(new CtMethod[h.size()]);
}
- private static void getMethods0(HashMap h, CtClass cc) {
+ private static void getMethods0(Map<String,CtMember> h, CtClass cc) {
try {
CtClass[] ifs = cc.getInterfaces();
- int size = ifs.length;
- for (int i = 0; i < size; ++i)
- getMethods0(h, ifs[i]);
+ for (CtClass ctc : ifs)
+ getMethods0(h, ctc);
}
catch (NotFoundException e) {}
@@ -1119,15 +1251,15 @@ class CtClassType extends CtClass {
}
}
+ @Override
public CtMethod getMethod(String name, String desc)
throws NotFoundException
{
CtMethod m = getMethod0(this, name, desc);
if (m != null)
return m;
- else
- throw new NotFoundException(name + "(..) is not found in "
- + getName());
+ throw new NotFoundException(name + "(..) is not found in "
+ + getName());
}
private static CtMethod getMethod0(CtClass cc,
@@ -1157,9 +1289,8 @@ class CtClassType extends CtClass {
try {
CtClass[] ifs = cc.getInterfaces();
- int size = ifs.length;
- for (int i = 0; i < size; ++i) {
- CtMethod m = getMethod0(ifs[i], name, desc);
+ for (CtClass ctc : ifs) {
+ CtMethod m = getMethod0(ctc, name, desc);
if (m != null)
return m;
}
@@ -1168,21 +1299,36 @@ class CtClassType extends CtClass {
return null;
}
+ @Override
public CtMethod[] getDeclaredMethods() {
CtMember.Cache memCache = getMembers();
CtMember mth = memCache.methodHead();
CtMember mthTail = memCache.lastMethod();
- int num = CtMember.Cache.count(mth, mthTail);
- CtMethod[] cms = new CtMethod[num];
- int i = 0;
+ List<CtMember> methods = new ArrayList<CtMember>();
+ while (mth != mthTail) {
+ mth = mth.next();
+ methods.add(mth);
+ }
+
+ return methods.toArray(new CtMethod[methods.size()]);
+ }
+
+ @Override
+ public CtMethod[] getDeclaredMethods(String name) throws NotFoundException {
+ CtMember.Cache memCache = getMembers();
+ CtMember mth = memCache.methodHead();
+ CtMember mthTail = memCache.lastMethod();
+ List<CtMember> methods = new ArrayList<CtMember>();
while (mth != mthTail) {
mth = mth.next();
- cms[i++] = (CtMethod)mth;
+ if (mth.getName().equals(name))
+ methods.add(mth);
}
- return cms;
+ return methods.toArray(new CtMethod[methods.size()]);
}
+ @Override
public CtMethod getDeclaredMethod(String name) throws NotFoundException {
CtMember.Cache memCache = getMembers();
CtMember mth = memCache.methodHead();
@@ -1197,6 +1343,7 @@ class CtClassType extends CtClass {
+ getName());
}
+ @Override
public CtMethod getDeclaredMethod(String name, CtClass[] params)
throws NotFoundException
{
@@ -1216,12 +1363,14 @@ class CtClassType extends CtClass {
+ getName());
}
+ @Override
public void addField(CtField f, String init)
throws CannotCompileException
{
addField(f, CtField.Initializer.byExpr(init));
}
+ @Override
public void addField(CtField f, CtField.Initializer init)
throws CannotCompileException
{
@@ -1264,6 +1413,7 @@ class CtClassType extends CtClass {
}
}
+ @Override
public void removeField(CtField f) throws NotFoundException {
checkModify();
FieldInfo fi = f.getFieldInfo2();
@@ -1276,6 +1426,7 @@ class CtClassType extends CtClass {
throw new NotFoundException(f.toString());
}
+ @Override
public CtConstructor makeClassInitializer()
throws CannotCompileException
{
@@ -1290,6 +1441,7 @@ class CtClassType extends CtClass {
return getClassInitializer();
}
+ @Override
public void addConstructor(CtConstructor c)
throws CannotCompileException
{
@@ -1301,6 +1453,7 @@ class CtClassType extends CtClass {
getClassFile2().addMethod(c.getMethodInfo2());
}
+ @Override
public void removeConstructor(CtConstructor m) throws NotFoundException {
checkModify();
MethodInfo mi = m.getMethodInfo2();
@@ -1313,6 +1466,7 @@ class CtClassType extends CtClass {
throw new NotFoundException(m.toString());
}
+ @Override
public void addMethod(CtMethod m) throws CannotCompileException {
checkModify();
if (m.getDeclaringClass() != this)
@@ -1320,10 +1474,11 @@ class CtClassType extends CtClass {
int mod = m.getModifiers();
if ((getModifiers() & Modifier.INTERFACE) != 0) {
- m.setModifiers(mod | Modifier.PUBLIC);
- if ((mod & Modifier.ABSTRACT) == 0)
+ if (Modifier.isProtected(mod) || Modifier.isPrivate(mod))
throw new CannotCompileException(
- "an interface method must be abstract: " + m.toString());
+ "an interface method must be public: " + m.toString());
+
+ m.setModifiers(mod | Modifier.PUBLIC);
}
getMembers().addMethod(m);
@@ -1332,7 +1487,9 @@ class CtClassType extends CtClass {
setModifiers(getModifiers() | Modifier.ABSTRACT);
}
- public void removeMethod(CtMethod m) throws NotFoundException {
+ @Override
+ public void removeMethod(CtMethod m) throws NotFoundException
+ {
checkModify();
MethodInfo mi = m.getMethodInfo2();
ClassFile cf = getClassFile2();
@@ -1344,52 +1501,53 @@ class CtClassType extends CtClass {
throw new NotFoundException(m.toString());
}
- public byte[] getAttribute(String name) {
+ @Override
+ public byte[] getAttribute(String name)
+ {
AttributeInfo ai = getClassFile2().getAttribute(name);
if (ai == null)
return null;
- else
- return ai.get();
+ return ai.get();
}
- public void setAttribute(String name, byte[] data) {
+ @Override
+ public void setAttribute(String name, byte[] data)
+ {
checkModify();
ClassFile cf = getClassFile2();
cf.addAttribute(new AttributeInfo(cf.getConstPool(), name, data));
}
+ @Override
public void instrument(CodeConverter converter)
throws CannotCompileException
{
checkModify();
ClassFile cf = getClassFile2();
ConstPool cp = cf.getConstPool();
- List list = cf.getMethods();
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ List<MethodInfo> methods = cf.getMethods();
+ for (MethodInfo minfo: methods.toArray(new MethodInfo[methods.size()]))
converter.doit(this, minfo, cp);
- }
}
+ @Override
public void instrument(ExprEditor editor)
throws CannotCompileException
{
checkModify();
ClassFile cf = getClassFile2();
- List list = cf.getMethods();
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ List<MethodInfo> methods = cf.getMethods();
+ for (MethodInfo minfo: methods.toArray(new MethodInfo[methods.size()]))
editor.doit(this, minfo);
- }
}
/**
* @see javassist.CtClass#prune()
* @see javassist.CtClass#stopPruning(boolean)
*/
- public void prune() {
+ @Override
+ public void prune()
+ {
if (wasPruned)
return;
@@ -1397,8 +1555,10 @@ class CtClassType extends CtClass {
getClassFile2().prune();
}
+ @Override
public void rebuildClassFile() { gcConstPool = true; }
+ @Override
public void toBytecode(DataOutputStream out)
throws CannotCompileException, IOException
{
@@ -1413,6 +1573,9 @@ class CtClassType extends CtClass {
modifyClassConstructor(cf);
modifyConstructors(cf);
+ if (debugDump != null)
+ dumpClassFile(cf);
+
cf.write(out);
out.flush();
fieldInitializers = null;
@@ -1439,15 +1602,29 @@ class CtClassType extends CtClass {
}
}
+ private void dumpClassFile(ClassFile cf) throws IOException
+ {
+ DataOutputStream dump = makeFileOutput(debugDump);
+ try {
+ cf.write(dump);
+ }
+ finally {
+ dump.close();
+ }
+ }
+
/* See also checkModified()
*/
- private void checkPruned(String method) {
+ private void checkPruned(String method)
+ {
if (wasPruned)
throw new RuntimeException(method + "(): " + getName()
+ " was pruned.");
}
- public boolean stopPruning(boolean stop) {
+ @Override
+ public boolean stopPruning(boolean stop)
+ {
boolean prev = !doPruning;
doPruning = !stop;
return prev;
@@ -1532,10 +1709,8 @@ class CtClassType extends CtClass {
return;
ConstPool cp = cf.getConstPool();
- List list = cf.getMethods();
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ List<MethodInfo> methods = cf.getMethods();
+ for (MethodInfo minfo:methods) {
if (minfo.isConstructor()) {
CodeAttribute codeAttr = minfo.getCodeAttribute();
if (codeAttr != null)
@@ -1606,19 +1781,20 @@ class CtClassType extends CtClass {
// Methods used by CtNewWrappedMethod
- Hashtable getHiddenMethods() {
+ Map<CtMethod,String> getHiddenMethods() {
if (hiddenMethods == null)
- hiddenMethods = new Hashtable();
+ hiddenMethods = new Hashtable<CtMethod,String>();
return hiddenMethods;
}
int getUniqueNumber() { return uniqueNumberSeed++; }
+ @Override
public String makeUniqueName(String prefix) {
- HashMap table = new HashMap();
+ Map<Object,CtClassType> table = new HashMap<Object,CtClassType>();
makeMemberList(table);
- Set keys = table.keySet();
+ Set<Object> keys = table.keySet();
String[] methods = new String[keys.size()];
keys.toArray(methods);
@@ -1645,17 +1821,14 @@ class CtClassType extends CtClass {
return true;
}
- private void makeMemberList(HashMap table) {
+ private void makeMemberList(Map<Object,CtClassType> table) {
int mod = getModifiers();
if (Modifier.isAbstract(mod) || Modifier.isInterface(mod))
try {
CtClass[] ifs = getInterfaces();
- int size = ifs.length;
- for (int i = 0; i < size; i++) {
- CtClass ic =ifs[i];
+ for (CtClass ic : ifs)
if (ic != null && ic instanceof CtClassType)
((CtClassType)ic).makeMemberList(table);
- }
}
catch (NotFoundException e) {}
@@ -1666,19 +1839,13 @@ class CtClassType extends CtClass {
}
catch (NotFoundException e) {}
- List list = getClassFile2().getMethods();
- int n = list.size();
- for (int i = 0; i < n; i++) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ List<MethodInfo> methods = getClassFile2().getMethods();
+ for (MethodInfo minfo:methods)
table.put(minfo.getName(), this);
- }
- list = getClassFile2().getFields();
- n = list.size();
- for (int i = 0; i < n; i++) {
- FieldInfo finfo = (FieldInfo)list.get(i);
+ List<FieldInfo> fields = getClassFile2().getFields();
+ for (FieldInfo finfo:fields)
table.put(finfo.getName(), this);
- }
}
}
diff --git a/src/main/javassist/CtConstructor.java b/src/main/javassist/CtConstructor.java
index a90b73c..8e293ca 100644
--- a/src/main/javassist/CtConstructor.java
+++ b/src/main/javassist/CtConstructor.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,17 @@
package javassist;
-import javassist.bytecode.*;
-import javassist.compiler.Javac;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
/**
* An instance of CtConstructor represents a constructor.
@@ -121,6 +130,7 @@ public final class CtConstructor extends CtBehavior {
*
* @since 3.5
*/
+ @Override
public String getLongName() {
return getDeclaringClass().getName()
+ (isConstructor() ? Descriptor.toString(getSignature())
@@ -133,11 +143,11 @@ public final class CtConstructor extends CtBehavior {
* constructor. If this object represents a class initializer,
* then this method returns <code>"&lt;clinit&gt;"</code>.
*/
+ @Override
public String getName() {
if (methodInfo.isStaticInitializer())
return MethodInfo.nameClinit;
- else
- return declaringClass.getSimpleName();
+ return declaringClass.getSimpleName();
}
/**
@@ -147,6 +157,7 @@ public final class CtConstructor extends CtBehavior {
* calling <code>super()</code> (the no-argument constructor of
* the super class).
*/
+ @Override
public boolean isEmpty() {
CodeAttribute ca = getMethodInfo2().getCodeAttribute();
if (ca == null)
@@ -206,6 +217,7 @@ public final class CtConstructor extends CtBehavior {
* constructor body does nothing except calling
* <code>super()</code>.
*/
+ @Override
public void setBody(String src) throws CannotCompileException {
if (src == null)
if (isClassInitializer())
@@ -279,6 +291,7 @@ public final class CtConstructor extends CtBehavior {
/* This method is called by addCatch() in CtBehavior.
* super() and this() must not be in a try statement.
*/
+ @Override
int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException {
CodeIterator ci = ca.iterator();
try {
diff --git a/src/main/javassist/CtField.java b/src/main/javassist/CtField.java
index c4af7e5..892fff8 100644
--- a/src/main/javassist/CtField.java
+++ b/src/main/javassist/CtField.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,13 +16,23 @@
package javassist;
-import javassist.bytecode.*;
+import java.util.List;
+
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.AnnotationsAttribute;
+import javassist.bytecode.AttributeInfo;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.FieldInfo;
+import javassist.bytecode.SignatureAttribute;
+import javassist.compiler.CompileError;
import javassist.compiler.Javac;
import javassist.compiler.SymbolTable;
-import javassist.compiler.CompileError;
import javassist.compiler.ast.ASTree;
-import javassist.compiler.ast.IntConst;
import javassist.compiler.ast.DoubleConst;
+import javassist.compiler.ast.IntConst;
import javassist.compiler.ast.StringL;
/**
@@ -82,15 +93,12 @@ public class CtField extends CtMember {
{
this(src.fieldInfo.getDescriptor(), src.fieldInfo.getName(),
declaring);
- java.util.ListIterator iterator
- = src.fieldInfo.getAttributes().listIterator();
FieldInfo fi = fieldInfo;
fi.setAccessFlags(src.fieldInfo.getAccessFlags());
ConstPool cp = fi.getConstPool();
- while (iterator.hasNext()) {
- AttributeInfo ainfo = (AttributeInfo)iterator.next();
+ List<AttributeInfo> attributes = src.fieldInfo.getAttributes();
+ for (AttributeInfo ainfo : attributes)
fi.addAttribute(ainfo.copy(cp, null));
- }
}
private CtField(String typeDesc, String name, CtClass clazz)
@@ -113,11 +121,13 @@ public class CtField extends CtMember {
/**
* Returns a String representation of the object.
*/
+ @Override
public String toString() {
return getDeclaringClass().getName() + "." + getName()
+ ":" + fieldInfo.getDescriptor();
}
+ @Override
protected void extendToString(StringBuffer buffer) {
buffer.append(' ');
buffer.append(getName());
@@ -125,27 +135,24 @@ public class CtField extends CtMember {
buffer.append(fieldInfo.getDescriptor());
}
- /* Javac.CtFieldWithInit overrides.
- */
+ /* Javac.CtFieldWithInit overrides. */
protected ASTree getInitAST() { return null; }
- /* Called by CtClassType.addField().
- */
+ /* Called by CtClassType.addField(). */
Initializer getInit() {
ASTree tree = getInitAST();
if (tree == null)
return null;
- else
- return Initializer.byExpr(tree);
+ return Initializer.byExpr(tree);
}
/**
* Compiles the given source code and creates a field.
* Examples of the source code are:
*
- * <ul><pre>
+ * <pre>
* "public String name;"
- * "public int k = 3;"</pre></ul>
+ * "public int k = 3;"</pre>
*
* <p>Note that the source code ends with <code>';'</code>
* (semicolon).
@@ -201,6 +208,7 @@ public class CtField extends CtMember {
/**
* Returns the class declaring the field.
*/
+ @Override
public CtClass getDeclaringClass() {
// this is redundant but for javadoc.
return super.getDeclaringClass();
@@ -209,6 +217,7 @@ public class CtField extends CtMember {
/**
* Returns the name of the field.
*/
+ @Override
public String getName() {
return fieldInfo.getName();
}
@@ -226,6 +235,7 @@ public class CtField extends CtMember {
*
* @see Modifier
*/
+ @Override
public int getModifiers() {
return AccessFlag.toModifier(fieldInfo.getAccessFlags());
}
@@ -235,25 +245,27 @@ public class CtField extends CtMember {
*
* @see Modifier
*/
+ @Override
public void setModifiers(int mod) {
declaringClass.checkModify();
fieldInfo.setAccessFlags(AccessFlag.of(mod));
}
/**
- * Returns true if the class has the specified annotation class.
+ * Returns true if the class has the specified annotation type.
*
- * @param clz the annotation class.
+ * @param typeName the name of annotation type.
* @return <code>true</code> if the annotation is found, otherwise <code>false</code>.
- * @since 3.11
+ * @since 3.21
*/
- public boolean hasAnnotation(Class clz) {
+ @Override
+ public boolean hasAnnotation(String typeName) {
FieldInfo fi = getFieldInfo2();
AnnotationsAttribute ainfo = (AnnotationsAttribute)
fi.getAttribute(AnnotationsAttribute.invisibleTag);
AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
fi.getAttribute(AnnotationsAttribute.visibleTag);
- return CtClassType.hasAnnotationType(clz, getDeclaringClass().getClassPool(),
+ return CtClassType.hasAnnotationType(typeName, getDeclaringClass().getClassPool(),
ainfo, ainfo2);
}
@@ -268,7 +280,8 @@ public class CtField extends CtMember {
* @return the annotation if found, otherwise <code>null</code>.
* @since 3.11
*/
- public Object getAnnotation(Class clz) throws ClassNotFoundException {
+ @Override
+ public Object getAnnotation(Class<?> clz) throws ClassNotFoundException {
FieldInfo fi = getFieldInfo2();
AnnotationsAttribute ainfo = (AnnotationsAttribute)
fi.getAttribute(AnnotationsAttribute.invisibleTag);
@@ -285,6 +298,7 @@ public class CtField extends CtMember {
* @see #getAvailableAnnotations()
* @since 3.1
*/
+ @Override
public Object[] getAnnotations() throws ClassNotFoundException {
return getAnnotations(false);
}
@@ -298,6 +312,7 @@ public class CtField extends CtMember {
* @see #getAnnotations()
* @since 3.3
*/
+ @Override
public Object[] getAvailableAnnotations(){
try {
return getAnnotations(true);
@@ -326,20 +341,47 @@ public class CtField extends CtMember {
*
* <p>Note that the returned string is not the type signature
* contained in the <code>SignatureAttirbute</code>. It is
- * a descriptor. To obtain a type signature, call the following
- * methods:
- *
- * <ul><pre>getFieldInfo().getAttribute(SignatureAttribute.tag)
- * </pre></ul>
+ * a descriptor.
*
* @see javassist.bytecode.Descriptor
- * @see javassist.bytecode.SignatureAttribute
+ * @see #getGenericSignature()
*/
+ @Override
public String getSignature() {
return fieldInfo.getDescriptor();
}
/**
+ * Returns the generic signature of the field.
+ * It represents a type including type variables.
+ *
+ * @see SignatureAttribute#toFieldSignature(String)
+ * @since 3.17
+ */
+ @Override
+ public String getGenericSignature() {
+ SignatureAttribute sa
+ = (SignatureAttribute)fieldInfo.getAttribute(SignatureAttribute.tag);
+ return sa == null ? null : sa.getSignature();
+ }
+
+ /**
+ * Set the generic signature of the field.
+ * It represents a type including type variables.
+ * See {@link javassist.CtClass#setGenericSignature(String)}
+ * for a code sample.
+ *
+ * @param sig a new generic signature.
+ * @see javassist.bytecode.SignatureAttribute.ObjectType#encode()
+ * @since 3.17
+ */
+ @Override
+ public void setGenericSignature(String sig) {
+ declaringClass.checkModify();
+ fieldInfo.addAttribute(new SignatureAttribute(fieldInfo.getConstPool(), sig));
+ }
+
+ /**
* Returns the type of the field.
*/
public CtClass getType() throws NotFoundException {
@@ -349,6 +391,17 @@ public class CtField extends CtMember {
/**
* Sets the type of the field.
+ *
+ * <p>This method does not automatically update method bodies that access
+ * this field. They have to be explicitly updated. For example,
+ * if some method contains an expression {@code t.value} and the type
+ * of the variable {@code t} is changed by {@link #setType(CtClass)}
+ * from {@code int} to {@code double}, then {@code t.value} has to be modified
+ * as well since the bytecode of {@code t.value} contains the type information.
+ * </p>
+ *
+ * @see CodeConverter
+ * @see javassist.expr.ExprEditor
*/
public void setType(CtClass clazz) {
declaringClass.checkModify();
@@ -380,18 +433,17 @@ public class CtField extends CtMember {
ConstPool cp = fieldInfo.getConstPool();
switch (cp.getTag(index)) {
case ConstPool.CONST_Long :
- return new Long(cp.getLongInfo(index));
+ return Long.valueOf(cp.getLongInfo(index));
case ConstPool.CONST_Float :
- return new Float(cp.getFloatInfo(index));
+ return Float.valueOf(cp.getFloatInfo(index));
case ConstPool.CONST_Double :
- return new Double(cp.getDoubleInfo(index));
+ return Double.valueOf(cp.getDoubleInfo(index));
case ConstPool.CONST_Integer :
int value = cp.getIntegerInfo(index);
// "Z" means boolean type.
if ("Z".equals(fieldInfo.getDescriptor()))
- return new Boolean(value != 0);
- else
- return new Integer(value);
+ return Boolean.valueOf(value != 0);
+ return Integer.valueOf(value);
case ConstPool.CONST_String :
return cp.getStringInfo(index);
default :
@@ -411,12 +463,12 @@ public class CtField extends CtMember {
*
* @param name attribute name
*/
+ @Override
public byte[] getAttribute(String name) {
AttributeInfo ai = fieldInfo.getAttribute(name);
if (ai == null)
return null;
- else
- return ai.get();
+ return ai.get();
}
/**
@@ -429,6 +481,7 @@ public class CtField extends CtMember {
* @param name attribute name
* @param data attribute value
*/
+ @Override
public void setAttribute(String name, byte[] data) {
declaringClass.checkModify();
fieldInfo.addAttribute(new AttributeInfo(fieldInfo.getConstPool(),
@@ -526,8 +579,7 @@ public class CtField extends CtMember {
* value of the field. The constructor of the created object receives
* the parameter:
*
- * <ul><code>Object obj</code> - the object including the field.<br>
- * </ul>
+ * <p><code>Object obj</code> - the object including the field.
*
* <p>If the initialized field is static, then the constructor does
* not receive any parameters.
@@ -549,10 +601,9 @@ public class CtField extends CtMember {
* value of the field. The constructor of the created object receives
* the parameters:
*
- * <ul><code>Object obj</code> - the object including the field.<br>
+ * <p><code>Object obj</code> - the object including the field.<br>
* <code>String[] strs</code> - the character strings specified
* by <code>stringParams</code><br>
- * </ul>
*
* <p>If the initialized field is static, then the constructor
* receives only <code>strs</code>.
@@ -577,11 +628,10 @@ public class CtField extends CtMember {
* value of the field. The constructor of the created object receives
* the parameters:
*
- * <ul><code>Object obj</code> - the object including the field.<br>
+ * <p><code>Object obj</code> - the object including the field.<br>
* <code>Object[] args</code> - the parameters passed to the
* constructor of the object including the
* filed.
- * </ul>
*
* <p>If the initialized field is static, then the constructor does
* not receive any parameters.
@@ -606,13 +656,12 @@ public class CtField extends CtMember {
* value of the field. The constructor of the created object receives
* the parameters:
*
- * <ul><code>Object obj</code> - the object including the field.<br>
+ * <p><code>Object obj</code> - the object including the field.<br>
* <code>String[] strs</code> - the character strings specified
* by <code>stringParams</code><br>
* <code>Object[] args</code> - the parameters passed to the
* constructor of the object including the
* filed.
- * </ul>
*
* <p>If the initialized field is static, then the constructor receives
* only <code>strs</code>.
@@ -637,8 +686,7 @@ public class CtField extends CtMember {
* value as the initial value of the field.
* The called method receives the parameters:
*
- * <ul><code>Object obj</code> - the object including the field.<br>
- * </ul>
+ * <p><code>Object obj</code> - the object including the field.
*
* <p>If the initialized field is static, then the method does
* not receive any parameters.
@@ -667,10 +715,9 @@ public class CtField extends CtMember {
* value as the initial value of the field. The called method
* receives the parameters:
*
- * <ul><code>Object obj</code> - the object including the field.<br>
+ * <p><code>Object obj</code> - the object including the field.<br>
* <code>String[] strs</code> - the character strings specified
* by <code>stringParams</code><br>
- * </ul>
*
* <p>If the initialized field is static, then the method
* receive only <code>strs</code>.
@@ -702,11 +749,10 @@ public class CtField extends CtMember {
* value as the initial value of the field. The called method
* receives the parameters:
*
- * <ul><code>Object obj</code> - the object including the field.<br>
+ * <p><code>Object obj</code> - the object including the field.<br>
* <code>Object[] args</code> - the parameters passed to the
* constructor of the object including the
* filed.
- * </ul>
*
* <p>If the initialized field is static, then the method does
* not receive any parameters.
@@ -735,13 +781,12 @@ public class CtField extends CtMember {
* value as the initial value of the field. The called method
* receives the parameters:
*
- * <ul><code>Object obj</code> - the object including the field.<br>
+ * <p><code>Object obj</code> - the object including the field.<br>
* <code>String[] strs</code> - the character strings specified
* by <code>stringParams</code><br>
* <code>Object[] args</code> - the parameters passed to the
* constructor of the object including the
* filed.
- * </ul>
*
* <p>If the initialized field is static, then the method
* receive only <code>strs</code>.
@@ -826,6 +871,7 @@ public class CtField extends CtMember {
static abstract class CodeInitializer0 extends Initializer {
abstract void compileExpr(Javac drv) throws CompileError;
+ @Override
int compile(CtClass type, String name, Bytecode code,
CtClass[] parameters, Javac drv)
throws CannotCompileException
@@ -841,6 +887,7 @@ public class CtField extends CtMember {
}
}
+ @Override
int compileIfStatic(CtClass type, String name, Bytecode code,
Javac drv) throws CannotCompileException
{
@@ -859,9 +906,9 @@ public class CtField extends CtMember {
if (tree instanceof IntConst) {
long value = ((IntConst)tree).get();
if (type == CtClass.doubleType)
- return cp.addDoubleInfo((double)value);
+ return cp.addDoubleInfo(value);
else if (type == CtClass.floatType)
- return cp.addFloatInfo((float)value);
+ return cp.addFloatInfo(value);
else if (type == CtClass.longType)
return cp.addLongInfo(value);
else if (type != CtClass.voidType)
@@ -888,10 +935,12 @@ public class CtField extends CtMember {
CodeInitializer(String expr) { expression = expr; }
+ @Override
void compileExpr(Javac drv) throws CompileError {
drv.compileExpr(expression);
}
+ @Override
int getConstantValue(ConstPool cp, CtClass type) {
try {
ASTree t = Javac.parseExpr(expression, new SymbolTable());
@@ -908,10 +957,12 @@ public class CtField extends CtMember {
PtreeInitializer(ASTree expr) { expression = expr; }
+ @Override
void compileExpr(Javac drv) throws CompileError {
drv.compileExpr(expression);
}
+ @Override
int getConstantValue(ConstPool cp, CtClass type) {
return getConstantValue2(cp, type, expression);
}
@@ -926,6 +977,7 @@ public class CtField extends CtMember {
ParamInitializer() {}
+ @Override
int compile(CtClass type, String name, Bytecode code,
CtClass[] parameters, Javac drv)
throws CannotCompileException
@@ -937,8 +989,7 @@ public class CtField extends CtMember {
code.addPutfield(Bytecode.THIS, name, Descriptor.of(type));
return s; // stack size
}
- else
- return 0; // do not initialize
+ return 0; // do not initialize
}
/**
@@ -970,6 +1021,7 @@ public class CtField extends CtMember {
return k;
}
+ @Override
int compileIfStatic(CtClass type, String name, Bytecode code,
Javac drv) throws CannotCompileException
{
@@ -991,6 +1043,7 @@ public class CtField extends CtMember {
* Produces codes in which a new object is created and assigned to
* the field as the initial value.
*/
+ @Override
int compile(CtClass type, String name, Bytecode code,
CtClass[] parameters, Javac drv)
throws CannotCompileException
@@ -1025,16 +1078,17 @@ public class CtField extends CtMember {
return "(Ljava/lang/Object;[Ljava/lang/Object;)V";
else
return "(Ljava/lang/Object;)V";
- else
- if (withConstructorParams)
- return desc3;
- else
- return "(Ljava/lang/Object;[Ljava/lang/String;)V";
+
+ if (withConstructorParams)
+ return desc3;
+
+ return "(Ljava/lang/Object;[Ljava/lang/String;)V";
}
/**
* Produces codes for a static field.
*/
+ @Override
int compileIfStatic(CtClass type, String name, Bytecode code,
Javac drv) throws CannotCompileException
{
@@ -1087,6 +1141,7 @@ public class CtField extends CtMember {
* Produces codes in which a new object is created and assigned to
* the field as the initial value.
*/
+ @Override
int compile(CtClass type, String name, Bytecode code,
CtClass[] parameters, Javac drv)
throws CannotCompileException
@@ -1121,16 +1176,17 @@ public class CtField extends CtMember {
return "(Ljava/lang/Object;[Ljava/lang/Object;)";
else
return "(Ljava/lang/Object;)";
- else
- if (withConstructorParams)
- return desc3;
- else
- return "(Ljava/lang/Object;[Ljava/lang/String;)";
+
+ if (withConstructorParams)
+ return desc3;
+
+ return "(Ljava/lang/Object;[Ljava/lang/String;)";
}
/**
* Produces codes for a static field.
*/
+ @Override
int compileIfStatic(CtClass type, String name, Bytecode code,
Javac drv) throws CannotCompileException
{
@@ -1156,12 +1212,14 @@ public class CtField extends CtMember {
IntInitializer(int v) { value = v; }
+ @Override
void check(String desc) throws CannotCompileException {
char c = desc.charAt(0);
if (c != 'I' && c != 'S' && c != 'B' && c != 'C' && c != 'Z')
throw new CannotCompileException("type mismatch");
}
+ @Override
int compile(CtClass type, String name, Bytecode code,
CtClass[] parameters, Javac drv)
throws CannotCompileException
@@ -1172,6 +1230,7 @@ public class CtField extends CtMember {
return 2; // stack size
}
+ @Override
int compileIfStatic(CtClass type, String name, Bytecode code,
Javac drv) throws CannotCompileException
{
@@ -1180,6 +1239,7 @@ public class CtField extends CtMember {
return 1; // stack size
}
+ @Override
int getConstantValue(ConstPool cp, CtClass type) {
return cp.addIntegerInfo(value);
}
@@ -1190,11 +1250,13 @@ public class CtField extends CtMember {
LongInitializer(long v) { value = v; }
+ @Override
void check(String desc) throws CannotCompileException {
if (!desc.equals("J"))
throw new CannotCompileException("type mismatch");
}
+ @Override
int compile(CtClass type, String name, Bytecode code,
CtClass[] parameters, Javac drv)
throws CannotCompileException
@@ -1205,6 +1267,7 @@ public class CtField extends CtMember {
return 3; // stack size
}
+ @Override
int compileIfStatic(CtClass type, String name, Bytecode code,
Javac drv) throws CannotCompileException
{
@@ -1213,11 +1276,11 @@ public class CtField extends CtMember {
return 2; // stack size
}
+ @Override
int getConstantValue(ConstPool cp, CtClass type) {
if (type == CtClass.longType)
return cp.addLongInfo(value);
- else
- return 0;
+ return 0;
}
}
@@ -1226,11 +1289,13 @@ public class CtField extends CtMember {
FloatInitializer(float v) { value = v; }
+ @Override
void check(String desc) throws CannotCompileException {
if (!desc.equals("F"))
throw new CannotCompileException("type mismatch");
}
+ @Override
int compile(CtClass type, String name, Bytecode code,
CtClass[] parameters, Javac drv)
throws CannotCompileException
@@ -1241,6 +1306,7 @@ public class CtField extends CtMember {
return 3; // stack size
}
+ @Override
int compileIfStatic(CtClass type, String name, Bytecode code,
Javac drv) throws CannotCompileException
{
@@ -1249,11 +1315,11 @@ public class CtField extends CtMember {
return 2; // stack size
}
+ @Override
int getConstantValue(ConstPool cp, CtClass type) {
if (type == CtClass.floatType)
return cp.addFloatInfo(value);
- else
- return 0;
+ return 0;
}
}
@@ -1262,11 +1328,13 @@ public class CtField extends CtMember {
DoubleInitializer(double v) { value = v; }
+ @Override
void check(String desc) throws CannotCompileException {
if (!desc.equals("D"))
throw new CannotCompileException("type mismatch");
}
+ @Override
int compile(CtClass type, String name, Bytecode code,
CtClass[] parameters, Javac drv)
throws CannotCompileException
@@ -1277,6 +1345,7 @@ public class CtField extends CtMember {
return 3; // stack size
}
+ @Override
int compileIfStatic(CtClass type, String name, Bytecode code,
Javac drv) throws CannotCompileException
{
@@ -1285,11 +1354,11 @@ public class CtField extends CtMember {
return 2; // stack size
}
+ @Override
int getConstantValue(ConstPool cp, CtClass type) {
if (type == CtClass.doubleType)
return cp.addDoubleInfo(value);
- else
- return 0;
+ return 0;
}
}
@@ -1298,6 +1367,7 @@ public class CtField extends CtMember {
StringInitializer(String v) { value = v; }
+ @Override
int compile(CtClass type, String name, Bytecode code,
CtClass[] parameters, Javac drv)
throws CannotCompileException
@@ -1308,6 +1378,7 @@ public class CtField extends CtMember {
return 2; // stack size
}
+ @Override
int compileIfStatic(CtClass type, String name, Bytecode code,
Javac drv) throws CannotCompileException
{
@@ -1316,11 +1387,11 @@ public class CtField extends CtMember {
return 1; // stack size
}
+ @Override
int getConstantValue(ConstPool cp, CtClass type) {
if (type.getName().equals(javaLangString))
return cp.addStringInfo(value);
- else
- return 0;
+ return 0;
}
}
@@ -1338,6 +1409,7 @@ public class CtField extends CtMember {
code.addAnewarray(type, size);
}
+ @Override
int compile(CtClass type, String name, Bytecode code,
CtClass[] parameters, Javac drv)
throws CannotCompileException
@@ -1348,6 +1420,7 @@ public class CtField extends CtMember {
return 2; // stack size
}
+ @Override
int compileIfStatic(CtClass type, String name, Bytecode code,
Javac drv) throws CannotCompileException
{
@@ -1363,11 +1436,13 @@ public class CtField extends CtMember {
MultiArrayInitializer(CtClass t, int[] d) { type = t; dim = d; }
+ @Override
void check(String desc) throws CannotCompileException {
if (desc.charAt(0) != '[')
throw new CannotCompileException("type mismatch");
}
+ @Override
int compile(CtClass type, String name, Bytecode code,
CtClass[] parameters, Javac drv)
throws CannotCompileException
@@ -1378,6 +1453,7 @@ public class CtField extends CtMember {
return s + 1; // stack size
}
+ @Override
int compileIfStatic(CtClass type, String name, Bytecode code,
Javac drv) throws CannotCompileException
{
diff --git a/src/main/javassist/CtMember.java b/src/main/javassist/CtMember.java
index 0956642..8de480c 100644
--- a/src/main/javassist/CtMember.java
+++ b/src/main/javassist/CtMember.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -28,19 +29,34 @@ public abstract class CtMember {
* at the same time.
*/
static class Cache extends CtMember {
+ @Override
protected void extendToString(StringBuffer buffer) {}
- public boolean hasAnnotation(Class clz) { return false; }
- public Object getAnnotation(Class clz)
+ @Override
+ public boolean hasAnnotation(String clz) { return false; }
+ @Override
+ public Object getAnnotation(Class<?> clz)
throws ClassNotFoundException { return null; }
+ @Override
public Object[] getAnnotations()
throws ClassNotFoundException { return null; }
+ @Override
public byte[] getAttribute(String name) { return null; }
+ @Override
public Object[] getAvailableAnnotations() { return null; }
+ @Override
public int getModifiers() { return 0; }
+ @Override
public String getName() { return null; }
+ @Override
public String getSignature() { return null; }
+ @Override
public void setAttribute(String name, byte[] data) {}
+ @Override
public void setModifiers(int mod) {}
+ @Override
+ public String getGenericSignature() { return null; }
+ @Override
+ public void setGenericSignature(String sig) {}
private CtMember methodTail;
private CtMember consTail; // constructor tail
@@ -117,8 +133,7 @@ public abstract class CtMember {
break;
}
- else
- m = m.next;
+ m = m.next;
}
}
}
@@ -138,6 +153,7 @@ public abstract class CtMember {
*/
void nameReplaced() {}
+ @Override
public String toString() {
StringBuffer buffer = new StringBuffer(getClass().getName());
buffer.append("@");
@@ -205,26 +221,37 @@ public abstract class CtMember {
public abstract void setModifiers(int mod);
/**
- * Returns true if the class has the specified annotation class.
+ * Returns true if the class has the specified annotation type.
*
- * @param clz the annotation class.
+ * @param clz the annotation type.
* @return <code>true</code> if the annotation is found, otherwise <code>false</code>.
* @since 3.11
*/
- public abstract boolean hasAnnotation(Class clz);
+ public boolean hasAnnotation(Class<?> clz) {
+ return hasAnnotation(clz.getName());
+ }
/**
- * Returns the annotation if the class has the specified annotation class.
+ * Returns true if the class has the specified annotation type.
+ *
+ * @param annotationTypeName the name of annotation type.
+ * @return <code>true</code> if the annotation is found, otherwise <code>false</code>.
+ * @since 3.21
+ */
+ public abstract boolean hasAnnotation(String annotationTypeName);
+
+ /**
+ * Returns the annotation if the class has the specified annotation type.
* For example, if an annotation <code>@Author</code> is associated
* with this member, an <code>Author</code> object is returned.
* The member values can be obtained by calling methods on
* the <code>Author</code> object.
*
- * @param clz the annotation class.
+ * @param annotationType the annotation type.
* @return the annotation if found, otherwise <code>null</code>.
* @since 3.11
*/
- public abstract Object getAnnotation(Class clz) throws ClassNotFoundException;
+ public abstract Object getAnnotation(Class<?> annotationType) throws ClassNotFoundException;
/**
* Returns the annotations associated with this member.
@@ -269,6 +296,27 @@ public abstract class CtMember {
public abstract String getSignature();
/**
+ * Returns the generic signature of the member.
+ *
+ * @see javassist.bytecode.SignatureAttribute#toFieldSignature(String)
+ * @see javassist.bytecode.SignatureAttribute#toMethodSignature(String)
+ * @see CtClass#getGenericSignature()
+ * @since 3.17
+ */
+ public abstract String getGenericSignature();
+
+ /**
+ * Sets the generic signature of the member.
+ *
+ * @param sig a new generic signature.
+ * @see javassist.bytecode.SignatureAttribute.ObjectType#encode()
+ * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode()
+ * @see CtClass#setGenericSignature(String)
+ * @since 3.17
+ */
+ public abstract void setGenericSignature(String sig);
+
+ /**
* Obtains a user-defined attribute with the given name.
* If that attribute is not found in the class file, this
* method returns null.
diff --git a/src/main/javassist/CtMethod.java b/src/main/javassist/CtMethod.java
index 727ff5b..3702e35 100644
--- a/src/main/javassist/CtMethod.java
+++ b/src/main/javassist/CtMethod.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,7 +16,15 @@
package javassist;
-import javassist.bytecode.*;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
/**
* An instance of <code>CtMethod</code> represents a method.
@@ -70,16 +79,18 @@ public final class CtMethod extends CtBehavior {
* <p>For example, suppose that a method <code>at()</code> is as
* follows:
*
- * <ul><pre>public X at(int i) {
+ * <pre>
+ * public X at(int i) {
* return (X)super.elementAt(i);
- * }</pre></ul>
+ * }</pre>
*
* <p>(<code>X</code> is a class name.) If <code>map</code> substitutes
* <code>String</code> for <code>X</code>, then the created method is:
*
- * <ul><pre>public String at(int i) {
+ * <pre>
+ * public String at(int i) {
* return (String)super.elementAt(i);
- * }</pre></ul>
+ * }</pre>
*
* <p>By default, all the occurrences of the names of the class
* declaring <code>at()</code> and the superclass are replaced
@@ -152,6 +163,7 @@ public final class CtMethod extends CtBehavior {
* If two methods have the same name and signature, then
* the hash codes for the two methods are equal.
*/
+ @Override
public int hashCode() {
return getStringRep().hashCode();
}
@@ -160,6 +172,7 @@ public final class CtMethod extends CtBehavior {
* This method is invoked when setName() or replaceClassName()
* in CtClass is called.
*/
+ @Override
void nameReplaced() {
cachedStringRep = null;
}
@@ -178,6 +191,7 @@ public final class CtMethod extends CtBehavior {
* Indicates whether <code>obj</code> has the same name and the
* same signature as this method.
*/
+ @Override
public boolean equals(Object obj) {
return obj != null && obj instanceof CtMethod
&& ((CtMethod)obj).getStringRep().equals(getStringRep());
@@ -189,6 +203,7 @@ public final class CtMethod extends CtBehavior {
*
* @since 3.5
*/
+ @Override
public String getLongName() {
return getDeclaringClass().getName() + "."
+ getName() + Descriptor.toString(getSignature());
@@ -197,6 +212,7 @@ public final class CtMethod extends CtBehavior {
/**
* Obtains the name of this method.
*/
+ @Override
public String getName() {
return methodInfo.getName();
}
@@ -220,6 +236,7 @@ public final class CtMethod extends CtBehavior {
* Returns true if the method body is empty, that is, <code>{}</code>.
* It also returns true if the method is an abstract method.
*/
+ @Override
public boolean isEmpty() {
CodeAttribute ca = getMethodInfo2().getCodeAttribute();
if (ca == null) // abstract or native
@@ -377,15 +394,18 @@ public final class CtMethod extends CtBehavior {
param = i;
}
+ @Override
int compile(Bytecode code) throws CannotCompileException {
code.addIconst(param);
return 1;
}
+ @Override
String descriptor() {
return "([Ljava/lang/Object;I)Ljava/lang/Object;";
}
+ @Override
String constDescriptor() {
return "([Ljava/lang/Object;I)V";
}
@@ -398,15 +418,18 @@ public final class CtMethod extends CtBehavior {
param = l;
}
+ @Override
int compile(Bytecode code) throws CannotCompileException {
code.addLconst(param);
return 2;
}
+ @Override
String descriptor() {
return "([Ljava/lang/Object;J)Ljava/lang/Object;";
}
+ @Override
String constDescriptor() {
return "([Ljava/lang/Object;J)V";
}
@@ -419,15 +442,18 @@ public final class CtMethod extends CtBehavior {
param = s;
}
+ @Override
int compile(Bytecode code) throws CannotCompileException {
code.addLdc(param);
return 1;
}
+ @Override
String descriptor() {
return "([Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
}
+ @Override
String constDescriptor() {
return "([Ljava/lang/Object;Ljava/lang/String;)V";
}
diff --git a/src/main/javassist/CtNewClass.java b/src/main/javassist/CtNewClass.java
index ba48e93..b0a1480 100644
--- a/src/main/javassist/CtNewClass.java
+++ b/src/main/javassist/CtNewClass.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -17,6 +18,7 @@ package javassist;
import java.io.DataOutputStream;
import java.io.IOException;
+
import javassist.bytecode.ClassFile;
class CtNewClass extends CtClassType {
@@ -42,6 +44,7 @@ class CtNewClass extends CtClassType {
hasConstructor = isInterface;
}
+ @Override
protected void extendToString(StringBuffer buffer) {
if (hasConstructor)
buffer.append("hasConstructor ");
@@ -49,6 +52,7 @@ class CtNewClass extends CtClassType {
super.extendToString(buffer);
}
+ @Override
public void addConstructor(CtConstructor c)
throws CannotCompileException
{
@@ -56,6 +60,7 @@ class CtNewClass extends CtClassType {
super.addConstructor(c);
}
+ @Override
public void toBytecode(DataOutputStream out)
throws CannotCompileException, IOException
{
@@ -116,8 +121,7 @@ class CtNewClass extends CtClassType {
String pname2 = superclazz.getPackageName();
if (pname == null)
return pname2 == null;
- else
- return pname.equals(pname2);
+ return pname.equals(pname2);
}
return true;
diff --git a/src/main/javassist/CtNewConstructor.java b/src/main/javassist/CtNewConstructor.java
index f510356..9f4225f 100644
--- a/src/main/javassist/CtNewConstructor.java
+++ b/src/main/javassist/CtNewConstructor.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,10 +16,11 @@
package javassist;
-import javassist.bytecode.*;
-import javassist.compiler.Javac;
-import javassist.compiler.CompileError;
import javassist.CtMethod.ConstParameter;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.ConstPool;
+import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
/**
* A collection of static methods for creating a <code>CtConstructor</code>.
@@ -218,8 +220,8 @@ public class CtNewConstructor {
* <code>Object</code>. The signature of the super's constructor
* must be:
*
- * <ul><code>constructor(Object[] params, &lt;type&gt; cvalue)
- * </code></ul>
+ * <pre>constructor(Object[] params, &lt;type&gt; cvalue)
+ * </pre>
*
* <p>Here, <code>cvalue</code> is the constant value specified
* by <code>cparam</code>.
@@ -227,7 +229,7 @@ public class CtNewConstructor {
* <p>If <code>cparam</code> is <code>null</code>, the signature
* must be:
*
- * <ul><code>constructor(Object[] params)</code></ul>
+ * <pre>constructor(Object[] params)</pre>
*
* <p>If <code>body</code> is not null, a copy of that method is
* embedded in the body of the created constructor.
@@ -244,20 +246,20 @@ public class CtNewConstructor {
* The method specified by <code>body</code> must have the
* signature shown below:
*
- * <ul><code>Object method(Object[] params, &lt;type&gt; cvalue)
- * </code></ul>
+ * <pre>Object method(Object[] params, &lt;type&gt; cvalue)</pre>
*
* <p>If <code>cparam</code> is <code>null</code>, the signature
* must be:
*
- * <ul><code>Object method(Object[] params)</code></ul>
+ * <pre>Object method(Object[] params)</pre>
*
* <p>Although the type of the returned value is <code>Object</code>,
* the value must be always <code>null</code>.
*
* <p><i>Example:</i>
*
- * <ul><pre>ClassPool pool = ... ;
+ * <pre>
+ * ClassPool pool = ... ;
* CtClass xclass = pool.makeClass("X");
* CtMethod method = pool.getMethod("Sample", "m");
* xclass.setSuperclass(pool.get("Y"));
@@ -265,20 +267,22 @@ public class CtNewConstructor {
* ConstParameter cparam = ConstParameter.string("test");
* CtConstructor c = CtNewConstructor.make(argTypes, null,
* PASS_PARAMS, method, cparam, xclass);
- * xclass.addConstructor(c);</pre></ul>
+ * xclass.addConstructor(c);</pre>
*
* <p>where the class <code>Sample</code> is as follows:
*
- * <ul><pre>public class Sample {
+ * <pre>
+ * public class Sample {
* public Object m(Object[] args, String msg) {
* System.out.println(msg);
* return null;
* }
- * }</pre></ul>
+ * }</pre>
*
* <p>This program produces the following class:
*
- * <ul><pre>public class X extends Y {
+ * <pre>
+ * public class X extends Y {
* public X(int p0) {
* super(p0);
* String msg = "test";
@@ -288,7 +292,7 @@ public class CtNewConstructor {
* Object result = null;
* // end
* }
- * }</pre></ul>
+ * }</pre>
*
* @param parameters a list of the parameter types
* @param exceptions a list of the exceptions
diff --git a/src/main/javassist/CtNewMethod.java b/src/main/javassist/CtNewMethod.java
index ec2a5fa..3daaa02 100644
--- a/src/main/javassist/CtNewMethod.java
+++ b/src/main/javassist/CtNewMethod.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,10 +16,15 @@
package javassist;
-import javassist.bytecode.*;
-import javassist.compiler.Javac;
-import javassist.compiler.CompileError;
import javassist.CtMethod.ConstParameter;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.ExceptionsAttribute;
+import javassist.bytecode.FieldInfo;
+import javassist.bytecode.MethodInfo;
+import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
/**
* A collection of static methods for creating a <code>CtMethod</code>.
@@ -33,7 +39,7 @@ public class CtNewMethod {
* The source code must include not only the method body
* but the whole declaration, for example,
*
- * <ul><pre>"public Object id(Object obj) { return obj; }"</pre></ul>
+ * <pre>"public Object id(Object obj) { return obj; }"</pre>
*
* @param src the source text.
* @param declaring the class to which the created method is added.
@@ -49,7 +55,7 @@ public class CtNewMethod {
* The source code must include not only the method body
* but the whole declaration, for example,
*
- * <ul><pre>"public Object id(Object obj) { return obj; }"</pre></ul>
+ * <pre>"public Object id(Object obj) { return obj; }"</pre>
*
* <p>If the source code includes <code>$proceed()</code>, then
* it is compiled into a method call on the specified object.
@@ -245,7 +251,9 @@ public class CtNewMethod {
}
minfo.setCodeAttribute(code.toCodeAttribute());
- return new CtMethod(minfo, field.getDeclaringClass());
+ CtClass cc = field.getDeclaringClass();
+ // a stack map is not needed.
+ return new CtMethod(minfo, cc);
}
/**
@@ -289,7 +297,9 @@ public class CtNewMethod {
}
minfo.setCodeAttribute(code.toCodeAttribute());
- return new CtMethod(minfo, field.getDeclaringClass());
+ CtClass cc = field.getDeclaringClass();
+ // a stack map is not needed.
+ return new CtMethod(minfo, cc);
}
/**
@@ -302,9 +312,10 @@ public class CtNewMethod {
*
* <p>The following method is an example of the created method.
*
- * <ul><pre>int f(int p, int q) {
+ * <pre>
+ * int f(int p, int q) {
* return super.f(p, q);
- * }</pre></ul>
+ * }</pre>
*
* <p>The name of the created method can be changed by
* <code>setName()</code>.
@@ -358,6 +369,7 @@ public class CtNewMethod {
code.setMaxLocals(++s);
code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value
minfo.setCodeAttribute(code.toCodeAttribute());
+ // a stack map is not needed.
return new CtMethod(minfo, declaring);
}
@@ -371,15 +383,14 @@ public class CtNewMethod {
*
* <p>The method specified by <code>body</code> must have this singature:
*
- * <ul><code>Object method(Object[] params, &lt;type&gt; cvalue)
- * </code></ul>
+ * <pre>Object method(Object[] params, &lt;type&gt; cvalue)</pre>
*
* <p>The type of the <code>cvalue</code> depends on
* <code>constParam</code>.
* If <code>constParam</code> is <code>null</code>, the signature
* must be:
*
- * <ul><code>Object method(Object[] params)</code></ul>
+ * <pre>Object method(Object[] params)</pre>
*
* <p>The method body copied from <code>body</code> is wrapped in
* parameter-conversion code, which converts parameters specified by
@@ -388,12 +399,13 @@ public class CtNewMethod {
* type to the type specified by <code>returnType</code>. Thus,
* the resulting method body is as follows:
*
- * <ul><pre>Object[] params = new Object[] { p0, p1, ... };
+ * <pre>
+ * Object[] params = new Object[] { p0, p1, ... };
* &lt;<i>type</i>&gt; cvalue = &lt;<i>constant-value</i>&gt;;
* <i>... copied method body ...</i>
* Object result = &lt;<i>returned value</i>&gt;
* return (<i>&lt;returnType&gt;</i>)result;
- * </pre></ul>
+ * </pre>
*
* <p>The variables <code>p0</code>, <code>p2</code>, ... represent
* formal parameters of the created method.
@@ -407,7 +419,8 @@ public class CtNewMethod {
*
* <p><i>Example:</i>
*
- * <ul><pre>ClassPool pool = ... ;
+ * <pre>
+ * ClassPool pool = ... ;
* CtClass vec = pool.makeClass("intVector");
* vec.setSuperclass(pool.get("java.util.Vector"));
* CtMethod addMethod = pool.getMethod("Sample", "add0");
@@ -415,20 +428,20 @@ public class CtNewMethod {
* CtClass[] argTypes = { CtClass.intType };
* CtMethod m = CtNewMethod.wrapped(CtClass.voidType, "add", argTypes,
* null, addMethod, null, vec);
- * vec.addMethod(m);</pre></ul>
+ * vec.addMethod(m);</pre>
*
* <p>where the class <code>Sample</code> is as follows:
*
- * <ul><pre>public class Sample extends java.util.Vector {
+ * <pre>public class Sample extends java.util.Vector {
* public Object add0(Object[] args) {
* super.addElement(args[0]);
* return null;
* }
- * }</pre></ul>
+ * }</pre>
*
* <p>This program produces a class <code>intVector</code>:
*
- * <ul><pre>public class intVector extends java.util.Vector {
+ * <pre>public class intVector extends java.util.Vector {
* public void add(int p0) {
* Object[] args = new Object[] { p0 };
* // begin of the copied body
@@ -436,7 +449,7 @@ public class CtNewMethod {
* Object result = null;
* // end
* }
- * }</pre></ul>
+ * }</pre>
*
* <p>Note that the type of the parameter to <code>add()</code> depends
* only on the value of <code>argTypes</code> passed to
diff --git a/src/main/javassist/CtNewNestedClass.java b/src/main/javassist/CtNewNestedClass.java
deleted file mode 100644
index 3611443..0000000
--- a/src/main/javassist/CtNewNestedClass.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- */
-
-package javassist;
-
-import javassist.bytecode.ClassFile;
-import javassist.bytecode.AccessFlag;
-import javassist.bytecode.InnerClassesAttribute;
-
-/**
- * A newly created public nested class.
- */
-class CtNewNestedClass extends CtNewClass {
- CtNewNestedClass(String realName, ClassPool cp, boolean isInterface,
- CtClass superclass) {
- super(realName, cp, isInterface, superclass);
- }
-
- /**
- * This method does not change the STATIC bit. The original value is kept.
- */
- public void setModifiers(int mod) {
- mod = mod & ~Modifier.STATIC;
- super.setModifiers(mod);
- updateInnerEntry(mod, getName(), this, true);
- }
-
- private static void updateInnerEntry(int mod, String name, CtClass clazz, boolean outer) {
- ClassFile cf = clazz.getClassFile2();
- InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute(
- InnerClassesAttribute.tag);
- if (ica == null)
- return;
-
- int n = ica.tableLength();
- for (int i = 0; i < n; i++)
- if (name.equals(ica.innerClass(i))) {
- int acc = ica.accessFlags(i) & AccessFlag.STATIC;
- ica.setAccessFlags(i, mod | acc);
- String outName = ica.outerClass(i);
- if (outName != null && outer)
- try {
- CtClass parent = clazz.getClassPool().get(outName);
- updateInnerEntry(mod, name, parent, false);
- }
- catch (NotFoundException e) {
- throw new RuntimeException("cannot find the declaring class: "
- + outName);
- }
-
- break;
- }
- }
-}
diff --git a/src/main/javassist/CtNewWrappedConstructor.java b/src/main/javassist/CtNewWrappedConstructor.java
index 78ab399..8e6b365 100644
--- a/src/main/javassist/CtNewWrappedConstructor.java
+++ b/src/main/javassist/CtNewWrappedConstructor.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,8 +16,10 @@
package javassist;
-import javassist.bytecode.*;
import javassist.CtMethod.ConstParameter;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.Descriptor;
class CtNewWrappedConstructor extends CtNewWrappedMethod {
private static final int PASS_NONE = CtNewConstructor.PASS_NONE;
@@ -38,6 +41,7 @@ class CtNewWrappedConstructor extends CtNewWrappedMethod {
howToCallSuper, body,
parameterTypes, constParam);
cons.getMethodInfo2().setCodeAttribute(code.toCodeAttribute());
+ // a stack map table is not needed.
return cons;
}
catch (NotFoundException e) {
diff --git a/src/main/javassist/CtNewWrappedMethod.java b/src/main/javassist/CtNewWrappedMethod.java
index 94cabd6..2e6a6a8 100644
--- a/src/main/javassist/CtNewWrappedMethod.java
+++ b/src/main/javassist/CtNewWrappedMethod.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,10 +16,16 @@
package javassist;
-import javassist.bytecode.*;
-import javassist.compiler.JvstCodeGen;
-import java.util.Hashtable;
+import java.util.Map;
+
import javassist.CtMethod.ConstParameter;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.SyntheticAttribute;
+import javassist.compiler.JvstCodeGen;
class CtNewWrappedMethod {
@@ -43,7 +50,9 @@ class CtNewWrappedMethod {
Bytecode code = makeBody(declaring, declaring.getClassFile2(), body,
parameterTypes, returnType, constParam);
- mt.getMethodInfo2().setCodeAttribute(code.toCodeAttribute());
+ MethodInfo minfo = mt.getMethodInfo2();
+ minfo.setCodeAttribute(code.toCodeAttribute());
+ // a stack map has been already created.
return mt;
}
@@ -136,8 +145,8 @@ class CtNewWrappedMethod {
CtMethod src)
throws BadBytecode, CannotCompileException
{
- Hashtable bodies = clazz.getHiddenMethods();
- String bodyname = (String)bodies.get(src);
+ Map<CtMethod,String> bodies = clazz.getHiddenMethods();
+ String bodyname = bodies.get(src);
if (bodyname == null) {
do {
bodyname = addedWrappedMethod + clazz.getUniqueNumber();
@@ -150,7 +159,7 @@ class CtNewWrappedMethod {
int acc = body.getAccessFlags();
body.setAccessFlags(AccessFlag.setPrivate(acc));
body.addAttribute(new SyntheticAttribute(classfile.getConstPool()));
- // a stack map is copied. rebuilding it is not needed.
+ // a stack map is copied. rebuilding it is not needed.
classfile.addMethod(body);
bodies.put(src, bodyname);
CtMember.Cache cache = clazz.hasMemberCache();
diff --git a/src/main/javassist/CtPrimitiveType.java b/src/main/javassist/CtPrimitiveType.java
index faf6700..24b5501 100644
--- a/src/main/javassist/CtPrimitiveType.java
+++ b/src/main/javassist/CtPrimitiveType.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -46,6 +47,7 @@ public final class CtPrimitiveType extends CtClass {
* Java type: boolean, byte, char, short, int, long, float, double,
* or void.
*/
+ @Override
public boolean isPrimitive() { return true; }
/**
@@ -54,6 +56,7 @@ public final class CtPrimitiveType extends CtClass {
*
* @see Modifier
*/
+ @Override
public int getModifiers() {
return Modifier.PUBLIC | Modifier.FINAL;
}
diff --git a/src/main/javassist/Loader.java b/src/main/javassist/Loader.java
index 160ef6e..37f83d6 100644
--- a/src/main/javassist/Loader.java
+++ b/src/main/javassist/Loader.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,10 +16,15 @@
package javassist;
-import java.io.*;
-import java.util.Hashtable;
-import java.util.Vector;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Vector;
+
+import javassist.bytecode.ClassFile;
/**
* The class loader for Javassist.
@@ -39,7 +45,7 @@ import java.security.ProtectionDomain;
* The startup program of an application using <code>MyTranslator</code>
* should be something like this:
*
- * <ul><pre>
+ * <pre>
* import javassist.*;
*
* public class Main {
@@ -51,15 +57,15 @@ import java.security.ProtectionDomain;
* cl.run("MyApp", args);
* }
* }
- * </pre></ul>
+ * </pre>
*
* <p>Class <code>MyApp</code> is the main program of the application.
*
* <p>This program should be executed as follows:
*
- * <ul><pre>
+ * <pre>
* % java Main <i>arg1</i> <i>arg2</i>...
- * </pre></ul>
+ * </pre>
*
* <p>It modifies the class <code>MyApp</code> with a <code>MyTranslator</code>
* object before the JVM loads it.
@@ -68,9 +74,9 @@ import java.security.ProtectionDomain;
*
* <p>This program execution is equivalent to:
*
- * <ul><pre>
+ * <pre>
* % java MyApp <i>arg1</i> <i>arg2</i>...
- * </pre></ul>
+ * </pre>
*
* <p>except that classes are translated by <code>MyTranslator</code>
* at load time.
@@ -80,12 +86,12 @@ import java.security.ProtectionDomain;
* unnecessary. For example, if only a class <code>test.Rectangle</code>
* is modified, the <code>main()</code> method above will be the following:
*
- * <ul><pre>
+ * <pre>
* ClassPool cp = ClassPool.getDefault();
* Loader cl = new Loader(cp);
* CtClass ct = cp.get("test.Rectangle");
* ct.setSuperclass(cp.get("test.Point"));
- * cl.run("MyApp", args);</pre></ul>
+ * cl.run("MyApp", args);</pre>
*
* <p>This program changes the super class of the <code>test.Rectangle</code>
* class.
@@ -133,8 +139,49 @@ import java.security.ProtectionDomain;
* @see javassist.Translator
*/
public class Loader extends ClassLoader {
- private Hashtable notDefinedHere; // must be atomic.
- private Vector notDefinedPackages; // must be atomic.
+
+ /**
+ * A simpler class loader.
+ * This is a class loader that exposes the protected {@code defineClass()} method
+ * declared in {@code java.lang.ClassLoader}. It provides a method similar to
+ * {@code CtClass#toClass()}.
+ *
+ * <p>When loading a class, this class loader delegates the work to the
+ * parent class loader unless the loaded classes are explicitly given
+ * by {@link #invokeDefineClass(CtClass)}.
+ * Note that a class {@code Foo} loaded by this class loader is
+ * different from the class with the same name {@code Foo} but loaded by
+ * another class loader. This is Java's naming rule.
+ * </p>
+ *
+ * @since 3.24
+ */
+ public static class Simple extends ClassLoader {
+ /**
+ * Constructs a class loader.
+ */
+ public Simple() {}
+
+ /**
+ * Constructs a class loader.
+ * @param parent the parent class loader.
+ */
+ public Simple(ClassLoader parent) {
+ super(parent);
+ }
+
+ /**
+ * Invokes the protected {@code defineClass()} in {@code ClassLoader}.
+ * It converts the given {@link CtClass} object into a {@code java.lang.Class} object.
+ */
+ public Class<?> invokeDefineClass(CtClass cc) throws IOException, CannotCompileException {
+ byte[] code = cc.toBytecode();
+ return defineClass(cc.getName(), code, 0, code.length);
+ }
+ }
+
+ private HashMap<String,ClassLoader> notDefinedHere; // must be atomic.
+ private Vector<String> notDefinedPackages; // must be atomic.
private ClassPool source;
private Translator translator;
private ProtectionDomain domain;
@@ -181,8 +228,8 @@ public class Loader extends ClassLoader {
}
private void init(ClassPool cp) {
- notDefinedHere = new Hashtable();
- notDefinedPackages = new Vector();
+ notDefinedHere = new HashMap<String,ClassLoader>();
+ notDefinedPackages = new Vector<String>();
source = cp;
translator = null;
domain = null;
@@ -244,11 +291,9 @@ public class Loader extends ClassLoader {
* <p>This method calls <code>run()</code>.
*
* @param args command line parameters.
- * <ul>
- * <code>args[0]</code> is the class name to be loaded.
- * <br><code>args[1..n]</code> are parameters passed
- * to the target <code>main()</code>.
- * </ul>
+ * <br>&nbsp;&nbsp;{@code args[0]} is the class name to be loaded.
+ * <br>&nbsp;&nbsp;{@code args[1..n]} are parameters passed
+ * to the target {@code main()}.
*
* @see javassist.Loader#run(String[])
*/
@@ -261,21 +306,14 @@ public class Loader extends ClassLoader {
* Loads a class and calls <code>main()</code> in that class.
*
* @param args command line parameters.
- * <ul>
- * <code>args[0]</code> is the class name to be loaded.
- * <br><code>args[1..n]</code> are parameters passed
- * to the target <code>main()</code>.
- * </ul>
+ *
+ * <br>&nbsp;&nbsp;{@code args[0]} is the class name to be loaded.
+ * <br>&nbsp;&nbsp;{@code args[1..n]} are parameters passed
+ * to the target {@code main()}.
*/
public void run(String[] args) throws Throwable {
- int n = args.length - 1;
- if (n >= 0) {
- String[] args2 = new String[n];
- for (int i = 0; i < n; ++i)
- args2[i] = args[i + 1];
-
- run(args[0], args2);
- }
+ if (args.length >= 1)
+ run(args[0], Arrays.copyOfRange(args, 1, args.length));
}
/**
@@ -285,13 +323,13 @@ public class Loader extends ClassLoader {
* @param args parameters passed to <code>main()</code>.
*/
public void run(String classname, String[] args) throws Throwable {
- Class c = loadClass(classname);
+ Class<?> c = loadClass(classname);
try {
- c.getDeclaredMethod("main", new Class[] { String[].class }).invoke(
+ c.getDeclaredMethod("main", new Class<?>[] { String[].class }).invoke(
null,
new Object[] { args });
}
- catch (java.lang.reflect.InvocationTargetException e) {
+ catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
@@ -299,11 +337,12 @@ public class Loader extends ClassLoader {
/**
* Requests the class loader to load a class.
*/
- protected Class loadClass(String name, boolean resolve)
+ @Override
+ protected Class<?> loadClass(String name, boolean resolve)
throws ClassFormatError, ClassNotFoundException {
name = name.intern();
synchronized (name) {
- Class c = findLoadedClass(name);
+ Class<?> c = findLoadedClass(name);
if (c == null)
c = loadClassByDelegation(name);
@@ -332,7 +371,8 @@ public class Loader extends ClassLoader {
* @throws ClassNotFoundException if an exception is thrown while
* obtaining a class file.
*/
- protected Class findClass(String name) throws ClassNotFoundException {
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classfile;
try {
if (source != null) {
@@ -364,7 +404,7 @@ public class Loader extends ClassLoader {
int i = name.lastIndexOf('.');
if (i != -1) {
String pname = name.substring(0, i);
- if (getPackage(pname) == null)
+ if (isDefinedPackage(pname))
try {
definePackage(
pname, null, null, null, null, null, null, null);
@@ -377,11 +417,17 @@ public class Loader extends ClassLoader {
if (domain == null)
return defineClass(name, classfile, 0, classfile.length);
+ return defineClass(name, classfile, 0, classfile.length, domain);
+ }
+
+ private boolean isDefinedPackage(String name) {
+ if (ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9)
+ return getDefinedPackage(name) == null;
else
- return defineClass(name, classfile, 0, classfile.length, domain);
+ return getPackage(name) == null;
}
- protected Class loadClassByDelegation(String name)
+ protected Class<?> loadClassByDelegation(String name)
throws ClassNotFoundException
{
/* The swing components must be loaded by a system
@@ -394,7 +440,7 @@ public class Loader extends ClassLoader {
* by this class loader.
*/
- Class c = null;
+ Class<?> c = null;
if (doDelegation)
if (name.startsWith("java.")
|| name.startsWith("javax.")
@@ -409,38 +455,22 @@ public class Loader extends ClassLoader {
}
private boolean notDelegated(String name) {
- if (notDefinedHere.get(name) != null)
+ if (notDefinedHere.containsKey(name))
return true;
- int n = notDefinedPackages.size();
- for (int i = 0; i < n; ++i)
- if (name.startsWith((String)notDefinedPackages.elementAt(i)))
+ for (String pack : notDefinedPackages)
+ if (name.startsWith(pack))
return true;
return false;
}
- protected Class delegateToParent(String classname)
+ protected Class<?> delegateToParent(String classname)
throws ClassNotFoundException
{
ClassLoader cl = getParent();
if (cl != null)
return cl.loadClass(classname);
- else
- return findSystemClass(classname);
- }
-
- protected Package getPackage(String name) {
- return super.getPackage(name);
- }
- /*
- // Package p = super.getPackage(name);
- Package p = null;
- if (p == null)
- return definePackage(name, null, null, null,
- null, null, null, null);
- else
- return p;
+ return findSystemClass(classname);
}
- */
}
diff --git a/src/main/javassist/LoaderClassPath.java b/src/main/javassist/LoaderClassPath.java
index 5ecb02e..6807dfa 100644
--- a/src/main/javassist/LoaderClassPath.java
+++ b/src/main/javassist/LoaderClassPath.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,8 +17,9 @@
package javassist;
import java.io.InputStream;
-import java.net.URL;
+import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
+import java.net.URL;
/**
* A class search-path representing a class loader.
@@ -30,6 +32,10 @@ import java.lang.ref.WeakReference;
*
* <p>The given class loader must have both <code>getResourceAsStream()</code>
* and <code>getResource()</code>.
+ *
+ * <p>Class files in a named module are private to that module.
+ * This method cannot obtain class files in named modules.
+ * </p>
*
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @author Shigeru Chiba
@@ -39,21 +45,18 @@ import java.lang.ref.WeakReference;
* @see ClassClassPath
*/
public class LoaderClassPath implements ClassPath {
- private WeakReference clref;
+ private Reference<ClassLoader> clref;
/**
* Creates a search path representing a class loader.
*/
public LoaderClassPath(ClassLoader cl) {
- clref = new WeakReference(cl);
+ clref = new WeakReference<ClassLoader>(cl);
}
+ @Override
public String toString() {
- Object cl = null;
- if (clref != null)
- cl = clref.get();
-
- return cl == null ? "<null>" : cl.toString();
+ return clref.get() == null ? "<null>" : clref.get().toString();
}
/**
@@ -61,13 +64,14 @@ public class LoaderClassPath implements ClassPath {
* This method calls <code>getResourceAsStream(String)</code>
* on the class loader.
*/
- public InputStream openClassfile(String classname) {
+ @Override
+ public InputStream openClassfile(String classname) throws NotFoundException {
String cname = classname.replace('.', '/') + ".class";
- ClassLoader cl = (ClassLoader)clref.get();
+ ClassLoader cl = clref.get();
if (cl == null)
return null; // not found
- else
- return cl.getResourceAsStream(cname);
+ InputStream is = cl.getResourceAsStream(cname);
+ return is;
}
/**
@@ -77,19 +81,13 @@ public class LoaderClassPath implements ClassPath {
*
* @return null if the class file could not be found.
*/
+ @Override
public URL find(String classname) {
String cname = classname.replace('.', '/') + ".class";
- ClassLoader cl = (ClassLoader)clref.get();
+ ClassLoader cl = clref.get();
if (cl == null)
return null; // not found
- else
- return cl.getResource(cname);
- }
-
- /**
- * Closes this class path.
- */
- public void close() {
- clref = null;
+ URL url = cl.getResource(cname);
+ return url;
}
}
diff --git a/src/main/javassist/Modifier.java b/src/main/javassist/Modifier.java
index c1b30d6..733cc67 100644
--- a/src/main/javassist/Modifier.java
+++ b/src/main/javassist/Modifier.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -45,7 +46,7 @@ public class Modifier {
public static final int ENUM = AccessFlag.ENUM;
/**
- * Returns true if the modifiers include the <tt>public</tt>
+ * Returns true if the modifiers include the <code>public</code>
* modifier.
*/
public static boolean isPublic(int mod) {
@@ -53,7 +54,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>private</tt>
+ * Returns true if the modifiers include the <code>private</code>
* modifier.
*/
public static boolean isPrivate(int mod) {
@@ -61,7 +62,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>protected</tt>
+ * Returns true if the modifiers include the <code>protected</code>
* modifier.
*/
public static boolean isProtected(int mod) {
@@ -70,14 +71,14 @@ public class Modifier {
/**
* Returns true if the modifiers do not include either
- * <tt>public</tt>, <tt>protected</tt>, or <tt>private</tt>.
+ * <code>public</code>, <code>protected</code>, or <code>private</code>.
*/
public static boolean isPackage(int mod) {
return (mod & (PUBLIC | PRIVATE | PROTECTED)) == 0;
}
/**
- * Returns true if the modifiers include the <tt>static</tt>
+ * Returns true if the modifiers include the <code>static</code>
* modifier.
*/
public static boolean isStatic(int mod) {
@@ -85,7 +86,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>final</tt>
+ * Returns true if the modifiers include the <code>final</code>
* modifier.
*/
public static boolean isFinal(int mod) {
@@ -93,7 +94,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>synchronized</tt>
+ * Returns true if the modifiers include the <code>synchronized</code>
* modifier.
*/
public static boolean isSynchronized(int mod) {
@@ -101,7 +102,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>volatile</tt>
+ * Returns true if the modifiers include the <code>volatile</code>
* modifier.
*/
public static boolean isVolatile(int mod) {
@@ -109,7 +110,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>transient</tt>
+ * Returns true if the modifiers include the <code>transient</code>
* modifier.
*/
public static boolean isTransient(int mod) {
@@ -117,7 +118,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>native</tt>
+ * Returns true if the modifiers include the <code>native</code>
* modifier.
*/
public static boolean isNative(int mod) {
@@ -125,7 +126,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>interface</tt>
+ * Returns true if the modifiers include the <code>interface</code>
* modifier.
*/
public static boolean isInterface(int mod) {
@@ -133,7 +134,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>annotation</tt>
+ * Returns true if the modifiers include the <code>annotation</code>
* modifier.
*
* @since 3.2
@@ -143,7 +144,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>enum</tt>
+ * Returns true if the modifiers include the <code>enum</code>
* modifier.
*
* @since 3.2
@@ -153,7 +154,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>abstract</tt>
+ * Returns true if the modifiers include the <code>abstract</code>
* modifier.
*/
public static boolean isAbstract(int mod) {
@@ -161,7 +162,7 @@ public class Modifier {
}
/**
- * Returns true if the modifiers include the <tt>strictfp</tt>
+ * Returns true if the modifiers include the <code>strictfp</code>
* modifier.
*/
public static boolean isStrict(int mod) {
@@ -169,6 +170,14 @@ public class Modifier {
}
/**
+ * Returns true if the modifiers include the <code>varargs</code>
+ * (variable number of arguments) modifier.
+ */
+ public static boolean isVarArgs(int mod) {
+ return (mod & VARARGS) != 0;
+ }
+
+ /**
* Truns the public bit on. The protected and private bits are
* cleared.
*/
diff --git a/src/main/javassist/NotFoundException.java b/src/main/javassist/NotFoundException.java
index 61de140..135d790 100644
--- a/src/main/javassist/NotFoundException.java
+++ b/src/main/javassist/NotFoundException.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -19,6 +20,9 @@ package javassist;
* Signals that something could not be found.
*/
public class NotFoundException extends Exception {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public NotFoundException(String msg) {
super(msg);
}
diff --git a/src/main/javassist/SerialVersionUID.java b/src/main/javassist/SerialVersionUID.java
index 5e62310..ebb18bf 100644
--- a/src/main/javassist/SerialVersionUID.java
+++ b/src/main/javassist/SerialVersionUID.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,12 +16,17 @@
package javassist;
-import java.io.*;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
import java.lang.reflect.Modifier;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Comparator;
-import javassist.bytecode.*;
-import java.util.*;
-import java.security.*;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.Descriptor;
/**
* Utility for calculating serialVersionUIDs for Serializable classes.
@@ -69,8 +75,10 @@ public class SerialVersionUID {
/**
* Calculate default value. See Java Serialization Specification, Stream
* Unique Identifiers.
+ *
+ * @since 3.20
*/
- static long calculateDefault(CtClass clazz)
+ public static long calculateDefault(CtClass clazz)
throws CannotCompileException
{
try {
@@ -105,16 +113,15 @@ public class SerialVersionUID {
// fields.
CtField[] fields = clazz.getDeclaredFields();
- Arrays.sort(fields, new Comparator() {
- public int compare(Object o1, Object o2) {
- CtField field1 = (CtField)o1;
- CtField field2 = (CtField)o2;
+ Arrays.sort(fields, new Comparator<CtField>() {
+ @Override
+ public int compare(CtField field1, CtField field2) {
return field1.getName().compareTo(field2.getName());
}
});
for (int i = 0; i < fields.length; i++) {
- CtField field = (CtField) fields[i];
+ CtField field = fields[i];
int mods = field.getModifiers();
if (((mods & Modifier.PRIVATE) == 0) ||
((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0)) {
@@ -133,10 +140,9 @@ public class SerialVersionUID {
// constructors.
CtConstructor[] constructors = clazz.getDeclaredConstructors();
- Arrays.sort(constructors, new Comparator() {
- public int compare(Object o1, Object o2) {
- CtConstructor c1 = (CtConstructor)o1;
- CtConstructor c2 = (CtConstructor)o2;
+ Arrays.sort(constructors, new Comparator<CtConstructor>() {
+ @Override
+ public int compare(CtConstructor c1, CtConstructor c2) {
return c1.getMethodInfo2().getDescriptor().compareTo(
c2.getMethodInfo2().getDescriptor());
}
@@ -154,10 +160,9 @@ public class SerialVersionUID {
}
// methods.
- Arrays.sort(methods, new Comparator() {
- public int compare(Object o1, Object o2) {
- CtMethod m1 = (CtMethod)o1;
- CtMethod m2 = (CtMethod)o2;
+ Arrays.sort(methods, new Comparator<CtMethod>() {
+ @Override
+ public int compare(CtMethod m1, CtMethod m2) {
int value = m1.getName().compareTo(m2.getName());
if (value == 0)
value = m1.getMethodInfo2().getDescriptor()
diff --git a/src/main/javassist/Translator.java b/src/main/javassist/Translator.java
index ea44034..c169594 100644
--- a/src/main/javassist/Translator.java
+++ b/src/main/javassist/Translator.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -46,8 +47,8 @@ public interface Translator {
* Is invoked by a <code>Loader</code> for notifying that
* a class is loaded. The <code>Loader</code> calls
*
- * <ul><pre>
- * pool.get(classname).toBytecode()</pre></ul>
+ * <pre>
+ * pool.get(classname).toBytecode()</pre>
*
* to read the class file after <code>onLoad()</code> returns.
*
diff --git a/src/main/javassist/URLClassPath.java b/src/main/javassist/URLClassPath.java
index 0cdb820..8e81c78 100644
--- a/src/main/javassist/URLClassPath.java
+++ b/src/main/javassist/URLClassPath.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,8 +16,11 @@
package javassist;
-import java.io.*;
-import java.net.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
/**
* A class search-path specified with URL (http).
@@ -40,8 +44,8 @@ public class URLClassPath implements ClassPath {
* "org.javassist.test.Main", then the given URL is used for loading that class.
* The <code>URLClassPath</code> obtains a class file from:
*
- * <ul><pre>http://www.javassist.org:80/java/classes/org/javassist/test/Main.class
- * </pre></ul>
+ * <pre>http://www.javassist.org:80/java/classes/org/javassist/test/Main.class
+ * </pre>
*
* <p>Here, we assume that <code>host</code> is "www.javassist.org",
* <code>port</code> is 80, and <code>directory</code> is "/java/classes/".
@@ -64,6 +68,7 @@ public class URLClassPath implements ClassPath {
this.packageName = packageName;
}
+ @Override
public String toString() {
return hostname + ":" + port + directory;
}
@@ -73,6 +78,7 @@ public class URLClassPath implements ClassPath {
*
* @return null if the class file could not be found.
*/
+ @Override
public InputStream openClassfile(String classname) {
try {
URLConnection con = openClassfile0(classname);
@@ -89,8 +95,7 @@ public class URLClassPath implements ClassPath {
= directory + classname.replace('.', '/') + ".class";
return fetchClass0(hostname, port, jarname);
}
- else
- return null; // not found
+ return null; // not found
}
/**
@@ -98,6 +103,7 @@ public class URLClassPath implements ClassPath {
*
* @return null if the class file could not be obtained.
*/
+ @Override
public URL find(String classname) {
try {
URLConnection con = openClassfile0(classname);
@@ -112,11 +118,6 @@ public class URLClassPath implements ClassPath {
}
/**
- * Closes this class path.
- */
- public void close() {}
-
- /**
* Reads a class file on an http server.
*
* @param host host name
diff --git a/src/main/javassist/bytecode/AccessFlag.java b/src/main/javassist/bytecode/AccessFlag.java
index 6dda112..244e29d 100644
--- a/src/main/javassist/bytecode/AccessFlag.java
+++ b/src/main/javassist/bytecode/AccessFlag.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -17,7 +18,7 @@ package javassist.bytecode;
/**
* A support class providing static methods and constants
- * for access modifiers such as public, rivate, ...
+ * for access modifiers such as public, private, ...
*/
public class AccessFlag {
public static final int PUBLIC = 0x0001;
@@ -37,14 +38,16 @@ public class AccessFlag {
public static final int SYNTHETIC = 0x1000;
public static final int ANNOTATION = 0x2000;
public static final int ENUM = 0x4000;
+ public static final int MANDATED = 0x8000;
public static final int SUPER = 0x0020;
+ public static final int MODULE = 0x8000;
// Note: 0x0020 is assigned to both ACC_SUPER and ACC_SYNCHRONIZED
// although java.lang.reflect.Modifier does not recognize ACC_SUPER.
/**
- * Truns the public bit on. The protected and private bits are
+ * Turns the public bit on. The protected and private bits are
* cleared.
*/
public static int setPublic(int accflags) {
@@ -52,7 +55,7 @@ public class AccessFlag {
}
/**
- * Truns the protected bit on. The protected and public bits are
+ * Turns the protected bit on. The protected and public bits are
* cleared.
*/
public static int setProtected(int accflags) {
diff --git a/src/main/javassist/bytecode/AnnotationDefaultAttribute.java b/src/main/javassist/bytecode/AnnotationDefaultAttribute.java
index d591ac8..43021ec 100644
--- a/src/main/javassist/bytecode/AnnotationDefaultAttribute.java
+++ b/src/main/javassist/bytecode/AnnotationDefaultAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,32 +16,32 @@
package javassist.bytecode;
-import javassist.CtClass;
-import javassist.bytecode.annotation.AnnotationsWriter;
-import javassist.bytecode.annotation.MemberValue;
-
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Map;
+import javassist.CtClass;
+import javassist.bytecode.annotation.AnnotationsWriter;
+import javassist.bytecode.annotation.MemberValue;
+
/**
* A class representing <code>AnnotationDefault_attribute</code>.
*
* <p>For example, if you declare the following annotation type:
*
- * <ul><pre>
+ * <pre>
* &#64;interface Author {
* String name() default "Shakespeare";
* int age() default 99;
* }
- * </pre></ul>
+ * </pre>
*
* <p>The defautl values of <code>name</code> and <code>age</code>
* are stored as annotation default attributes in <code>Author.class</code>.
* The following code snippet obtains the default value of <code>name</code>:
*
- * <ul><pre>
+ * <pre>
* ClassPool pool = ...
* CtClass cc = pool.get("Author");
* CtMethod cm = cc.getDeclaredMethod("age");
@@ -49,14 +50,14 @@ import java.util.Map;
* = (AnnotationDefaultAttribute)
* minfo.getAttribute(AnnotationDefaultAttribute.tag);
* MemberValue value = ada.getDefaultValue()); // default value of age
- * </pre></ul>
+ * </pre>
*
* <p>If the following statement is executed after the code above,
* the default value of age is set to 80:
*
- * <ul><pre>
+ * <pre>
* ada.setDefaultValue(new IntegerMemberValue(minfo.getConstPool(), 80));
- * </pre></ul>
+ * </pre>
*
* @see AnnotationsAttribute
* @see javassist.bytecode.annotation.MemberValue
@@ -103,7 +104,8 @@ public class AnnotationDefaultAttribute extends AttributeInfo {
/**
* Copies this attribute and returns a new copy.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
AnnotationsAttribute.Copier copier
= new AnnotationsAttribute.Copier(info, constPool, newCp, classnames);
try {
@@ -153,6 +155,7 @@ public class AnnotationDefaultAttribute extends AttributeInfo {
/**
* Returns a string representation of this object.
*/
+ @Override
public String toString() {
return getDefaultValue().toString();
}
diff --git a/src/main/javassist/bytecode/AnnotationsAttribute.java b/src/main/javassist/bytecode/AnnotationsAttribute.java
index 0d2ac09..73fcd73 100644
--- a/src/main/javassist/bytecode/AnnotationsAttribute.java
+++ b/src/main/javassist/bytecode/AnnotationsAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,12 +16,28 @@
package javassist.bytecode;
-import java.util.Map;
-import java.util.HashMap;
-import java.io.IOException;
-import java.io.DataInputStream;
import java.io.ByteArrayOutputStream;
-import javassist.bytecode.annotation.*;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javassist.bytecode.annotation.Annotation;
+import javassist.bytecode.annotation.AnnotationMemberValue;
+import javassist.bytecode.annotation.AnnotationsWriter;
+import javassist.bytecode.annotation.ArrayMemberValue;
+import javassist.bytecode.annotation.BooleanMemberValue;
+import javassist.bytecode.annotation.ByteMemberValue;
+import javassist.bytecode.annotation.CharMemberValue;
+import javassist.bytecode.annotation.ClassMemberValue;
+import javassist.bytecode.annotation.DoubleMemberValue;
+import javassist.bytecode.annotation.EnumMemberValue;
+import javassist.bytecode.annotation.FloatMemberValue;
+import javassist.bytecode.annotation.IntegerMemberValue;
+import javassist.bytecode.annotation.LongMemberValue;
+import javassist.bytecode.annotation.MemberValue;
+import javassist.bytecode.annotation.ShortMemberValue;
+import javassist.bytecode.annotation.StringMemberValue;
/**
* A class representing
@@ -38,7 +55,7 @@ import javassist.bytecode.annotation.*;
*
* <p>For example,
*
- * <ul><pre>
+ * <pre>
* import javassist.bytecode.annotation.Annotation;
* :
* CtMethod m = ... ;
@@ -48,7 +65,7 @@ import javassist.bytecode.annotation.*;
* Annotation an = attr.getAnnotation("Author");
* String s = ((StringMemberValue)an.getMemberValue("name")).getValue();
* System.out.println("@Author(name=" + s + ")");
- * </pre></ul>
+ * </pre>
*
* <p>This code snippet retrieves an annotation of the type <code>Author</code>
* from the <code>MethodInfo</code> object specified by <code>minfo</code>.
@@ -56,17 +73,17 @@ import javassist.bytecode.annotation.*;
*
* <p>If the annotation type <code>Author</code> is annotated by a meta annotation:
*
- * <ul><pre>
+ * <pre>
* &#64;Retention(RetentionPolicy.RUNTIME)
- * </pre></ul>
+ * </pre>
*
* <p>Then <code>Author</code> is visible at runtime. Therefore, the third
* statement of the code snippet above must be changed into:
*
- * <ul><pre>
+ * <pre>
* AnnotationsAttribute attr = (AnnotationsAttribute)
* minfo.getAttribute(AnnotationsAttribute.visibleTag);
- * </pre></ul>
+ * </pre>
*
* <p>The attribute tag must be <code>visibleTag</code> instead of
* <code>invisibleTag</code>.
@@ -82,7 +99,7 @@ import javassist.bytecode.annotation.*;
* <p>If you want to record a new AnnotationAttribute object, execute the
* following snippet:
*
- * <ul><pre>
+ * <pre>
* ClassFile cf = ... ;
* ConstPool cp = cf.getConstPool();
* AnnotationsAttribute attr
@@ -92,10 +109,10 @@ import javassist.bytecode.annotation.*;
* attr.setAnnotation(a);
* cf.addAttribute(attr);
* cf.setVersionToJava5();
- * </pre></ul>
+ * </pre>
*
* <p>The last statement is necessary if the class file was produced by
- * Javassist or JDK 1.4. Otherwise, it is not necessary.
+ * <code>javac</code> of JDK 1.4 or earlier. Otherwise, it is not necessary.
*
* @see AnnotationDefaultAttribute
* @see javassist.bytecode.annotation.Annotation
@@ -159,7 +176,8 @@ public class AnnotationsAttribute extends AttributeInfo {
/**
* Copies this attribute and returns a new copy.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
Copier copier = new Copier(info, constPool, newCp, classnames);
try {
copier.annotationArray();
@@ -213,6 +231,32 @@ public class AnnotationsAttribute extends AttributeInfo {
}
/**
+ * Removes an annotation by type.
+ * After removing an annotation, if {@link #numAnnotations()} returns 0,
+ * this annotations attribute has to be removed.
+ *
+ * @param type of annotation to remove
+ * @return whether an annotation with the given type has been removed
+ * @since 3.21
+ */
+ public boolean removeAnnotation(String type) {
+ Annotation[] annotations = getAnnotations();
+ for (int i = 0; i < annotations.length; i++) {
+ if (annotations[i].getTypeName().equals(type)) {
+ Annotation[] newlist = new Annotation[annotations.length - 1];
+ System.arraycopy(annotations, 0, newlist, 0, i);
+ if (i < annotations.length - 1) {
+ System.arraycopy(annotations, i + 1, newlist, i,
+ annotations.length - i - 1);
+ }
+ setAnnotations(newlist);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Parses the annotations and returns a data structure representing
* that parsed annotations. Note that changes of the node values of the
* returned tree are not reflected on the annotations represented by
@@ -257,7 +301,7 @@ public class AnnotationsAttribute extends AttributeInfo {
/**
* Changes the annotations. A call to this method is equivalent to:
- * <ul><pre>setAnnotations(new Annotation[] { annotation })</pre></ul>
+ * <pre>setAnnotations(new Annotation[] { annotation })</pre>
*
* @param annotation the data structure representing
* the new annotation.
@@ -270,13 +314,15 @@ public class AnnotationsAttribute extends AttributeInfo {
* @param oldname a JVM class name.
* @param newname a JVM class name.
*/
+ @Override
void renameClass(String oldname, String newname) {
- HashMap map = new HashMap();
+ Map<String,String> map = new HashMap<String,String>();
map.put(oldname, newname);
renameClass(map);
}
- void renameClass(Map classnames) {
+ @Override
+ void renameClass(Map<String,String> classnames) {
Renamer renamer = new Renamer(info, getConstPool(), classnames);
try {
renamer.annotationArray();
@@ -285,11 +331,13 @@ public class AnnotationsAttribute extends AttributeInfo {
}
}
- void getRefClasses(Map classnames) { renameClass(classnames); }
+ @Override
+ void getRefClasses(Map<String,String> classnames) { renameClass(classnames); }
/**
* Returns a string representation of this object.
*/
+ @Override
public String toString() {
Annotation[] a = getAnnotations();
StringBuilder sbuf = new StringBuilder();
@@ -349,15 +397,24 @@ public class AnnotationsAttribute extends AttributeInfo {
return pos;
}
+ /**
+ * {@code element_value_paris}
+ */
final int memberValuePair(int pos) throws Exception {
int nameIndex = ByteArray.readU16bit(info, pos);
return memberValuePair(pos + 2, nameIndex);
}
+ /**
+ * {@code element_value_paris[]}
+ */
int memberValuePair(int pos, int nameIndex) throws Exception {
return memberValue(pos);
}
+ /**
+ * {@code element_value}
+ */
final int memberValue(int pos) throws Exception {
int tag = info[pos] & 0xff;
if (tag == 'e') {
@@ -384,18 +441,33 @@ public class AnnotationsAttribute extends AttributeInfo {
}
}
+ /**
+ * {@code const_value_index}
+ */
void constValueMember(int tag, int index) throws Exception {}
+ /**
+ * {@code enum_const_value}
+ */
void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
throws Exception {
}
+ /**
+ * {@code class_info_index}
+ */
void classMemberValue(int pos, int index) throws Exception {}
+ /**
+ * {@code annotation_value}
+ */
int annotationMemberValue(int pos) throws Exception {
return annotation(pos);
}
+ /**
+ * {@code array_value}
+ */
int arrayMemberValue(int pos, int num) throws Exception {
for (int i = 0; i < num; ++i) {
pos = memberValue(pos);
@@ -407,7 +479,7 @@ public class AnnotationsAttribute extends AttributeInfo {
static class Renamer extends Walker {
ConstPool cpool;
- Map classnames;
+ Map<String,String> classnames;
/**
* Constructs a renamer. It renames some class names
@@ -418,17 +490,19 @@ public class AnnotationsAttribute extends AttributeInfo {
* @param map pairs of replaced and substituted class names.
* It can be null.
*/
- Renamer(byte[] info, ConstPool cp, Map map) {
+ Renamer(byte[] info, ConstPool cp, Map<String,String> map) {
super(info);
cpool = cp;
classnames = map;
}
+ @Override
int annotation(int pos, int type, int numPairs) throws Exception {
renameType(pos - 4, type);
return super.annotation(pos, type, numPairs);
}
+ @Override
void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
throws Exception
{
@@ -436,6 +510,7 @@ public class AnnotationsAttribute extends AttributeInfo {
super.enumMemberValue(pos, typeNameIndex, constNameIndex);
}
+ @Override
void classMemberValue(int pos, int index) throws Exception {
renameType(pos + 1, index);
super.classMemberValue(pos, index);
@@ -455,7 +530,7 @@ public class AnnotationsAttribute extends AttributeInfo {
ByteArrayOutputStream output;
AnnotationsWriter writer;
ConstPool srcPool, destPool;
- Map classnames;
+ Map<String,String> classnames;
/**
* Constructs a copier. This copier renames some class names
@@ -468,10 +543,16 @@ public class AnnotationsAttribute extends AttributeInfo {
* @param map pairs of replaced and substituted class names.
* It can be null.
*/
- Copier(byte[] info, ConstPool src, ConstPool dest, Map map) {
+ Copier(byte[] info, ConstPool src, ConstPool dest, Map<String,String> map) {
+ this(info, src, dest, map, true);
+ }
+
+ Copier(byte[] info, ConstPool src, ConstPool dest, Map<String,String> map, boolean makeWriter) {
super(info);
output = new ByteArrayOutputStream();
- writer = new AnnotationsWriter(output, dest);
+ if (makeWriter)
+ writer = new AnnotationsWriter(output, dest);
+
srcPool = src;
destPool = dest;
classnames = map;
@@ -482,31 +563,37 @@ public class AnnotationsAttribute extends AttributeInfo {
return output.toByteArray();
}
+ @Override
void parameters(int numParam, int pos) throws Exception {
writer.numParameters(numParam);
super.parameters(numParam, pos);
}
+ @Override
int annotationArray(int pos, int num) throws Exception {
writer.numAnnotations(num);
return super.annotationArray(pos, num);
}
+ @Override
int annotation(int pos, int type, int numPairs) throws Exception {
writer.annotation(copyType(type), numPairs);
return super.annotation(pos, type, numPairs);
}
+ @Override
int memberValuePair(int pos, int nameIndex) throws Exception {
writer.memberValuePair(copy(nameIndex));
return super.memberValuePair(pos, nameIndex);
}
+ @Override
void constValueMember(int tag, int index) throws Exception {
writer.constValueIndex(tag, copy(index));
super.constValueMember(tag, index);
}
+ @Override
void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
throws Exception
{
@@ -514,16 +601,19 @@ public class AnnotationsAttribute extends AttributeInfo {
super.enumMemberValue(pos, typeNameIndex, constNameIndex);
}
+ @Override
void classMemberValue(int pos, int index) throws Exception {
writer.classInfoIndex(copyType(index));
super.classMemberValue(pos, index);
}
+ @Override
int annotationMemberValue(int pos) throws Exception {
writer.annotationValue();
return super.annotationMemberValue(pos);
}
+ @Override
int arrayMemberValue(int pos, int num) throws Exception {
writer.arrayValue(num);
return super.arrayMemberValue(pos, num);
@@ -593,6 +683,7 @@ public class AnnotationsAttribute extends AttributeInfo {
return currentMember;
}
+ @Override
void parameters(int numParam, int pos) throws Exception {
Annotation[][] params = new Annotation[numParam][];
for (int i = 0; i < numParam; ++i) {
@@ -603,6 +694,7 @@ public class AnnotationsAttribute extends AttributeInfo {
allParams = params;
}
+ @Override
int annotationArray(int pos, int num) throws Exception {
Annotation[] array = new Annotation[num];
for (int i = 0; i < num; ++i) {
@@ -614,17 +706,20 @@ public class AnnotationsAttribute extends AttributeInfo {
return pos;
}
+ @Override
int annotation(int pos, int type, int numPairs) throws Exception {
currentAnno = new Annotation(type, pool);
return super.annotation(pos, type, numPairs);
}
+ @Override
int memberValuePair(int pos, int nameIndex) throws Exception {
pos = super.memberValuePair(pos, nameIndex);
currentAnno.addMemberValue(nameIndex, currentMember);
return pos;
}
+ @Override
void constValueMember(int tag, int index) throws Exception {
MemberValue m;
ConstPool cp = pool;
@@ -664,6 +759,7 @@ public class AnnotationsAttribute extends AttributeInfo {
super.constValueMember(tag, index);
}
+ @Override
void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
throws Exception
{
@@ -672,11 +768,13 @@ public class AnnotationsAttribute extends AttributeInfo {
super.enumMemberValue(pos, typeNameIndex, constNameIndex);
}
+ @Override
void classMemberValue(int pos, int index) throws Exception {
currentMember = new ClassMemberValue(index, pool);
super.classMemberValue(pos, index);
}
+ @Override
int annotationMemberValue(int pos) throws Exception {
Annotation anno = currentAnno;
pos = super.annotationMemberValue(pos);
@@ -685,6 +783,7 @@ public class AnnotationsAttribute extends AttributeInfo {
return pos;
}
+ @Override
int arrayMemberValue(int pos, int num) throws Exception {
ArrayMemberValue amv = new ArrayMemberValue(pool);
MemberValue[] elements = new MemberValue[num];
diff --git a/src/main/javassist/bytecode/AttributeInfo.java b/src/main/javassist/bytecode/AttributeInfo.java
index c5da7e1..be6e2a2 100644
--- a/src/main/javassist/bytecode/AttributeInfo.java
+++ b/src/main/javassist/bytecode/AttributeInfo.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -18,17 +19,20 @@ package javassist.bytecode;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
-import java.util.Map;
import java.util.ArrayList;
-import java.util.ListIterator;
+import java.util.Arrays;
import java.util.List;
-import java.util.Iterator;
+import java.util.Map;
// Note: if you define a new subclass of AttributeInfo, then
// update AttributeInfo.read(), .copy(), and (maybe) write().
/**
* <code>attribute_info</code> structure.
+ *
+ * @see ClassFile#getAttribute(String)
+ * @see MethodInfo#getAttribute(String)
+ * @see FieldInfo#getAttribute(String)
*/
public class AttributeInfo {
protected ConstPool constPool;
@@ -73,40 +77,54 @@ public class AttributeInfo {
{
int name = in.readUnsignedShort();
String nameStr = cp.getUtf8Info(name);
- if (nameStr.charAt(0) < 'L') {
+ char first = nameStr.charAt(0);
+ if (first < 'E')
if (nameStr.equals(AnnotationDefaultAttribute.tag))
return new AnnotationDefaultAttribute(cp, name, in);
+ else if (nameStr.equals(BootstrapMethodsAttribute.tag))
+ return new BootstrapMethodsAttribute(cp, name, in);
else if (nameStr.equals(CodeAttribute.tag))
return new CodeAttribute(cp, name, in);
else if (nameStr.equals(ConstantAttribute.tag))
return new ConstantAttribute(cp, name, in);
else if (nameStr.equals(DeprecatedAttribute.tag))
return new DeprecatedAttribute(cp, name, in);
- else if (nameStr.equals(EnclosingMethodAttribute.tag))
+
+ if (first < 'M')
+ if (nameStr.equals(EnclosingMethodAttribute.tag))
return new EnclosingMethodAttribute(cp, name, in);
else if (nameStr.equals(ExceptionsAttribute.tag))
return new ExceptionsAttribute(cp, name, in);
else if (nameStr.equals(InnerClassesAttribute.tag))
return new InnerClassesAttribute(cp, name, in);
- }
- else {
- /* Note that the names of Annotations attributes begin with 'R'.
- */
- if (nameStr.equals(LineNumberAttribute.tag))
+ else if (nameStr.equals(LineNumberAttribute.tag))
return new LineNumberAttribute(cp, name, in);
else if (nameStr.equals(LocalVariableAttribute.tag))
return new LocalVariableAttribute(cp, name, in);
else if (nameStr.equals(LocalVariableTypeAttribute.tag))
return new LocalVariableTypeAttribute(cp, name, in);
+
+ if (first < 'S')
+ /* Note that the names of Annotations attributes begin with 'R'. */
+ if (nameStr.equals(MethodParametersAttribute.tag))
+ return new MethodParametersAttribute(cp, name, in);
+ else if (nameStr.equals(NestHostAttribute.tag))
+ return new NestHostAttribute(cp, name, in);
+ else if (nameStr.equals(NestMembersAttribute.tag))
+ return new NestMembersAttribute(cp, name, in);
else if (nameStr.equals(AnnotationsAttribute.visibleTag)
- || nameStr.equals(AnnotationsAttribute.invisibleTag)) {
+ || nameStr.equals(AnnotationsAttribute.invisibleTag))
// RuntimeVisibleAnnotations or RuntimeInvisibleAnnotations
return new AnnotationsAttribute(cp, name, in);
- }
else if (nameStr.equals(ParameterAnnotationsAttribute.visibleTag)
- || nameStr.equals(ParameterAnnotationsAttribute.invisibleTag))
+ || nameStr.equals(ParameterAnnotationsAttribute.invisibleTag))
return new ParameterAnnotationsAttribute(cp, name, in);
- else if (nameStr.equals(SignatureAttribute.tag))
+ else if (nameStr.equals(TypeAnnotationsAttribute.visibleTag)
+ || nameStr.equals(TypeAnnotationsAttribute.invisibleTag))
+ return new TypeAnnotationsAttribute(cp, name, in);
+
+ if (first >= 'S')
+ if (nameStr.equals(SignatureAttribute.tag))
return new SignatureAttribute(cp, name, in);
else if (nameStr.equals(SourceFileAttribute.tag))
return new SourceFileAttribute(cp, name, in);
@@ -116,7 +134,6 @@ public class AttributeInfo {
return new StackMap(cp, name, in);
else if (nameStr.equals(StackMapTable.tag))
return new StackMapTable(cp, name, in);
- }
return new AttributeInfo(cp, name, in);
}
@@ -168,83 +185,68 @@ public class AttributeInfo {
* @param classnames pairs of replaced and substituted
* class names.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
- int s = info.length;
- byte[] srcInfo = info;
- byte[] newInfo = new byte[s];
- for (int i = 0; i < s; ++i)
- newInfo[i] = srcInfo[i];
-
- return new AttributeInfo(newCp, getName(), newInfo);
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames)
+ {
+ return new AttributeInfo(newCp, getName(), Arrays.copyOf(info, info.length));
}
- void write(DataOutputStream out) throws IOException {
+ void write(DataOutputStream out) throws IOException
+ {
out.writeShort(name);
out.writeInt(info.length);
if (info.length > 0)
out.write(info);
}
- static int getLength(ArrayList list) {
+ static int getLength(List<AttributeInfo> attributes) {
int size = 0;
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- AttributeInfo attr = (AttributeInfo)list.get(i);
+
+ for (AttributeInfo attr:attributes)
size += attr.length();
- }
return size;
}
- static AttributeInfo lookup(ArrayList list, String name) {
- if (list == null)
+ static AttributeInfo lookup(List<AttributeInfo> attributes, String name) {
+ if (attributes == null)
return null;
- ListIterator iterator = list.listIterator();
- while (iterator.hasNext()) {
- AttributeInfo ai = (AttributeInfo)iterator.next();
+ for (AttributeInfo ai:attributes)
if (ai.getName().equals(name))
return ai;
- }
return null; // no such attribute
}
- static synchronized void remove(ArrayList list, String name) {
- if (list == null)
- return;
+ static synchronized AttributeInfo remove(List<AttributeInfo> attributes, String name) {
+ if (attributes == null)
+ return null;
- ListIterator iterator = list.listIterator();
- while (iterator.hasNext()) {
- AttributeInfo ai = (AttributeInfo)iterator.next();
+ for (AttributeInfo ai:attributes)
if (ai.getName().equals(name))
- iterator.remove();
- }
+ if (attributes.remove(ai))
+ return ai;
+
+ return null;
}
- static void writeAll(ArrayList list, DataOutputStream out)
+ static void writeAll(List<AttributeInfo> attributes, DataOutputStream out)
throws IOException
{
- if (list == null)
+ if (attributes == null)
return;
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- AttributeInfo attr = (AttributeInfo)list.get(i);
+ for (AttributeInfo attr:attributes)
attr.write(out);
- }
}
- static ArrayList copyAll(ArrayList list, ConstPool cp) {
- if (list == null)
+ static List<AttributeInfo> copyAll(List<AttributeInfo> attributes, ConstPool cp) {
+ if (attributes == null)
return null;
- ArrayList newList = new ArrayList();
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- AttributeInfo attr = (AttributeInfo)list.get(i);
+ List<AttributeInfo> newList = new ArrayList<AttributeInfo>();
+ for (AttributeInfo attr:attributes)
newList.add(attr.copy(cp, null));
- }
return newList;
}
@@ -256,31 +258,31 @@ public class AttributeInfo {
* override these methods.
*/
void renameClass(String oldname, String newname) {}
- void renameClass(Map classnames) {}
+ void renameClass(Map<String,String> classnames) {}
+
+ static void renameClass(List<AttributeInfo> attributes, String oldname, String newname) {
+ if (attributes == null)
+ return;
- static void renameClass(List attributes, String oldname, String newname) {
- Iterator iterator = attributes.iterator();
- while (iterator.hasNext()) {
- AttributeInfo ai = (AttributeInfo)iterator.next();
+ for (AttributeInfo ai:attributes)
ai.renameClass(oldname, newname);
- }
}
- static void renameClass(List attributes, Map classnames) {
- Iterator iterator = attributes.iterator();
- while (iterator.hasNext()) {
- AttributeInfo ai = (AttributeInfo)iterator.next();
+ static void renameClass(List<AttributeInfo> attributes, Map<String,String> classnames) {
+ if (attributes == null)
+ return;
+
+ for (AttributeInfo ai:attributes)
ai.renameClass(classnames);
- }
}
- void getRefClasses(Map classnames) {}
+ void getRefClasses(Map<String,String> classnames) {}
+
+ static void getRefClasses(List<AttributeInfo> attributes, Map<String,String> classnames) {
+ if (attributes == null)
+ return;
- static void getRefClasses(List attributes, Map classnames) {
- Iterator iterator = attributes.iterator();
- while (iterator.hasNext()) {
- AttributeInfo ai = (AttributeInfo)iterator.next();
+ for (AttributeInfo ai:attributes)
ai.getRefClasses(classnames);
- }
}
}
diff --git a/src/main/javassist/bytecode/BadBytecode.java b/src/main/javassist/bytecode/BadBytecode.java
index 2f93b52..7a049b4 100644
--- a/src/main/javassist/bytecode/BadBytecode.java
+++ b/src/main/javassist/bytecode/BadBytecode.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -19,6 +20,9 @@ package javassist.bytecode;
* Signals that a bad bytecode sequence has been found.
*/
public class BadBytecode extends Exception {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public BadBytecode(int opcode) {
super("bytecode " + opcode);
}
@@ -30,4 +34,10 @@ public class BadBytecode extends Exception {
public BadBytecode(String msg, Throwable cause) {
super(msg, cause);
}
+
+ public BadBytecode(MethodInfo minfo, Throwable cause) {
+ super(minfo.toString() + " in "
+ + minfo.getConstPool().getClassName()
+ + ": " + cause.getMessage(), cause);
+ }
}
diff --git a/src/main/javassist/bytecode/BootstrapMethodsAttribute.java b/src/main/javassist/bytecode/BootstrapMethodsAttribute.java
new file mode 100644
index 0000000..94a0481
--- /dev/null
+++ b/src/main/javassist/bytecode/BootstrapMethodsAttribute.java
@@ -0,0 +1,124 @@
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Map;
+
+public class BootstrapMethodsAttribute extends AttributeInfo {
+ /**
+ * The name of this attribute <code>"BootstrapMethods"</code>.
+ */
+ public static final String tag = "BootstrapMethods";
+
+ /**
+ * An element of <code>bootstrap_methods</code>.
+ */
+ public static class BootstrapMethod {
+ /**
+ * Constructs an element of <code>bootstrap_methods</code>.
+ *
+ * @param method <code>bootstrap_method_ref</code>.
+ * @param args <code>bootstrap_arguments</code>.
+ */
+ public BootstrapMethod(int method, int[] args) {
+ methodRef = method;
+ arguments = args;
+ }
+
+ /**
+ * <code>bootstrap_method_ref</code>.
+ * The value at this index must be a <code>CONSTANT_MethodHandle_info</code>.
+ */
+ public int methodRef;
+
+ /**
+ * <code>bootstrap_arguments</code>.
+ */
+ public int[] arguments;
+ }
+
+ BootstrapMethodsAttribute(ConstPool cp, int n, DataInputStream in)
+ throws IOException
+ {
+ super(cp, n, in);
+ }
+
+ /**
+ * Constructs a BootstrapMethods attribute.
+ *
+ * @param cp a constant pool table.
+ * @param methods the contents.
+ */
+ public BootstrapMethodsAttribute(ConstPool cp, BootstrapMethod[] methods) {
+ super(cp, tag);
+ int size = 2;
+ for (int i = 0; i < methods.length; i++)
+ size += 4 + methods[i].arguments.length * 2;
+
+ byte[] data = new byte[size];
+ ByteArray.write16bit(methods.length, data, 0); // num_bootstrap_methods
+ int pos = 2;
+ for (int i = 0; i < methods.length; i++) {
+ ByteArray.write16bit(methods[i].methodRef, data, pos);
+ ByteArray.write16bit(methods[i].arguments.length, data, pos + 2);
+ int[] args = methods[i].arguments;
+ pos += 4;
+ for (int k = 0; k < args.length; k++) {
+ ByteArray.write16bit(args[k], data, pos);
+ pos += 2;
+ }
+ }
+
+ set(data);
+ }
+
+ /**
+ * Obtains <code>bootstrap_methods</code> in this attribute.
+ *
+ * @return an array of <code>BootstrapMethod</code>. Since it
+ * is a fresh copy, modifying the returned array does not
+ * affect the original contents of this attribute.
+ */
+ public BootstrapMethod[] getMethods() {
+ byte[] data = this.get();
+ int num = ByteArray.readU16bit(data, 0);
+ BootstrapMethod[] methods = new BootstrapMethod[num];
+ int pos = 2;
+ for (int i = 0; i < num; i++) {
+ int ref = ByteArray.readU16bit(data, pos);
+ int len = ByteArray.readU16bit(data, pos + 2);
+ int[] args = new int[len];
+ pos += 4;
+ for (int k = 0; k < len; k++) {
+ args[k] = ByteArray.readU16bit(data, pos);
+ pos += 2;
+ }
+
+ methods[i] = new BootstrapMethod(ref, args);
+ }
+
+ return methods;
+ }
+
+ /**
+ * Makes a copy. Class names are replaced according to the
+ * given <code>Map</code> object.
+ *
+ * @param newCp the constant pool table used by the new copy.
+ * @param classnames pairs of replaced and substituted
+ * class names.
+ */
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
+ BootstrapMethod[] methods = getMethods();
+ ConstPool thisCp = getConstPool();
+ for (int i = 0; i < methods.length; i++) {
+ BootstrapMethod m = methods[i];
+ m.methodRef = thisCp.copy(m.methodRef, newCp, classnames);
+ for (int k = 0; k < m.arguments.length; k++)
+ m.arguments[k] = thisCp.copy(m.arguments[k], newCp, classnames);
+ }
+
+ return new BootstrapMethodsAttribute(newCp, methods);
+ }
+}
diff --git a/src/main/javassist/bytecode/ByteArray.java b/src/main/javassist/bytecode/ByteArray.java
index e564822..1ca01ac 100644
--- a/src/main/javassist/bytecode/ByteArray.java
+++ b/src/main/javassist/bytecode/ByteArray.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/bytecode/ByteStream.java b/src/main/javassist/bytecode/ByteStream.java
index 82b30c9..ee48591 100644
--- a/src/main/javassist/bytecode/ByteStream.java
+++ b/src/main/javassist/bytecode/ByteStream.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,8 +16,8 @@
package javassist.bytecode;
-import java.io.OutputStream;
import java.io.IOException;
+import java.io.OutputStream;
final class ByteStream extends OutputStream {
private byte[] buf;
@@ -37,16 +38,19 @@ final class ByteStream extends OutputStream {
count += len;
}
+ @Override
public void write(byte[] data) {
write(data, 0, data.length);
}
+ @Override
public void write(byte[] data, int off, int len) {
enlarge(len);
System.arraycopy(data, off, buf, count, len);
count += len;
}
+ @Override
public void write(int b) {
enlarge(1);
int oldCount = count;
diff --git a/src/main/javassist/bytecode/Bytecode.java b/src/main/javassist/bytecode/Bytecode.java
index 92fd1f0..37a6d12 100644
--- a/src/main/javassist/bytecode/Bytecode.java
+++ b/src/main/javassist/bytecode/Bytecode.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -27,6 +28,7 @@ class ByteVector implements Cloneable {
size = 0;
}
+ @Override
public Object clone() throws CloneNotSupportedException {
ByteVector bv = (ByteVector)super.clone();
bv.buffer = (byte[])buffer.clone();
@@ -95,17 +97,19 @@ class ByteVector implements Cloneable {
* <p>A <code>Bytecode</code> object is an unbounded array
* containing bytecode. For example,
*
- * <ul><pre>ConstPool cp = ...; // constant pool table
+ * <pre>
+ * ConstPool cp = ...; // constant pool table
* Bytecode b = new Bytecode(cp, 1, 0);
* b.addIconst(3);
* b.addReturn(CtClass.intType);
- * CodeAttribute ca = b.toCodeAttribute();</ul></pre>
+ * CodeAttribute ca = b.toCodeAttribute();</pre>
*
* <p>This program produces a Code attribute including a bytecode
* sequence:
*
- * <ul><pre>iconst_3
- * ireturn</pre></ul>
+ * <pre>
+ * iconst_3
+ * ireturn</pre>
*
* @see ConstPool
* @see CodeAttribute
@@ -161,6 +165,7 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode {
* The constant pool object is shared between this object
* and the cloned object.
*/
+ @Override
public Object clone() {
try {
Bytecode bc = (Bytecode)super.clone();
@@ -322,6 +327,7 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode {
*
* @throws ArrayIndexOutOfBoundsException if offset is invalid.
*/
+ @Override
public int read(int offset) {
return super.read(offset);
}
@@ -352,6 +358,7 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode {
*
* @throws ArrayIndexOutOfBoundsException if offset is invalid.
*/
+ @Override
public void write(int offset, int value) {
super.write(offset, value);
}
@@ -377,6 +384,7 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode {
/**
* Appends an 8bit value to the end of the bytecode sequence.
*/
+ @Override
public void add(int code) {
super.add(code);
}
@@ -393,6 +401,7 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode {
*
* @param length the gap length in byte.
*/
+ @Override
public void addGap(int length) {
super.addGap(length);
}
@@ -923,11 +932,14 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode {
* @see Descriptor#ofConstructor(CtClass[])
*/
public void addInvokespecial(CtClass clazz, String name, String desc) {
- addInvokespecial(constPool.addClassInfo(clazz), name, desc);
+ boolean isInterface = clazz == null ? false : clazz.isInterface();
+ addInvokespecial(isInterface,
+ constPool.addClassInfo(clazz), name, desc);
}
/**
- * Appends INVOKESPECIAL.
+ * Appends INVOKESPECIAL. The invoked method must not be a default
+ * method declared in an interface.
*
* @param clazz the fully-qualified class name.
* @param name the method name
@@ -937,11 +949,12 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode {
* @see Descriptor#ofConstructor(CtClass[])
*/
public void addInvokespecial(String clazz, String name, String desc) {
- addInvokespecial(constPool.addClassInfo(clazz), name, desc);
+ addInvokespecial(false, constPool.addClassInfo(clazz), name, desc);
}
/**
- * Appends INVOKESPECIAL.
+ * Appends INVOKESPECIAL. The invoked method must not be a default
+ * method declared in an interface.
*
* @param clazz the index of <code>CONSTANT_Class_info</code>
* structure.
@@ -952,8 +965,45 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode {
* @see Descriptor#ofConstructor(CtClass[])
*/
public void addInvokespecial(int clazz, String name, String desc) {
+ addInvokespecial(false, clazz, name, desc);
+ }
+
+ /**
+ * Appends INVOKESPECIAL.
+ *
+ * @param isInterface true if the invoked method is a default method
+ * declared in an interface.
+ * @param clazz the index of <code>CONSTANT_Class_info</code>
+ * structure.
+ * @param name the method name
+ * @param desc the descriptor of the method signature.
+ *
+ * @see Descriptor#ofMethod(CtClass,CtClass[])
+ * @see Descriptor#ofConstructor(CtClass[])
+ */
+ public void addInvokespecial(boolean isInterface, int clazz, String name, String desc) {
+ int index;
+ if (isInterface)
+ index = constPool.addInterfaceMethodrefInfo(clazz, name, desc);
+ else
+ index = constPool.addMethodrefInfo(clazz, name, desc);
+
+ addInvokespecial(index, desc);
+ }
+
+ /**
+ * Appends INVOKESPECIAL.
+ *
+ * @param index the index of <code>CONSTANT_Methodref_info</code>
+ * or <code>CONSTANT_InterfaceMethodref_info</code>
+ * @param desc the descriptor of the method signature.
+ *
+ * @see Descriptor#ofMethod(CtClass,CtClass[])
+ * @see Descriptor#ofConstructor(CtClass[])
+ */
+ public void addInvokespecial(int index, String desc) {
add(INVOKESPECIAL);
- addIndex(constPool.addMethodrefInfo(clazz, name, desc));
+ addIndex(index);
growStack(Descriptor.dataSize(desc) - 1);
}
@@ -981,13 +1031,20 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode {
* @see Descriptor#ofMethod(CtClass,CtClass[])
*/
public void addInvokestatic(CtClass clazz, String name, String desc) {
- addInvokestatic(constPool.addClassInfo(clazz), name, desc);
+ boolean isInterface;
+ if (clazz == THIS)
+ isInterface = false;
+ else
+ isInterface = clazz.isInterface();
+
+ addInvokestatic(constPool.addClassInfo(clazz), name, desc, isInterface);
}
/**
* Appends INVOKESTATIC.
*
* @param classname the fully-qualified class name.
+ * It must not be an interface-type name.
* @param name the method name
* @param desc the descriptor of the method signature.
*
@@ -1001,15 +1058,26 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode {
* Appends INVOKESTATIC.
*
* @param clazz the index of <code>CONSTANT_Class_info</code>
- * structure.
+ * structure. It must not be an interface type.
* @param name the method name
* @param desc the descriptor of the method signature.
*
* @see Descriptor#ofMethod(CtClass,CtClass[])
*/
public void addInvokestatic(int clazz, String name, String desc) {
+ addInvokestatic(clazz, name, desc, false);
+ }
+
+ private void addInvokestatic(int clazz, String name, String desc,
+ boolean isInterface) {
add(INVOKESTATIC);
- addIndex(constPool.addMethodrefInfo(clazz, name, desc));
+ int index;
+ if (isInterface)
+ index = constPool.addInterfaceMethodrefInfo(clazz, name, desc);
+ else
+ index = constPool.addMethodrefInfo(clazz, name, desc);
+
+ addIndex(index);
growStack(Descriptor.dataSize(desc));
}
@@ -1154,6 +1222,25 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode {
}
/**
+ * Appends INVOKEDYNAMIC.
+ *
+ * @param bootstrap an index into the <code>bootstrap_methods</code> array
+ * of the bootstrap method table.
+ * @param name the method name.
+ * @param desc the method descriptor.
+ * @see Descriptor#ofMethod(CtClass,CtClass[])
+ * @since 3.17
+ */
+ public void addInvokedynamic(int bootstrap, String name, String desc) {
+ int nt = constPool.addNameAndTypeInfo(name, desc);
+ int dyn = constPool.addInvokeDynamicInfo(bootstrap, nt);
+ add(INVOKEDYNAMIC);
+ addIndex(dyn);
+ add(0, 0);
+ growStack(Descriptor.dataSize(desc)); // assume ConstPool#REF_invokeStatic
+ }
+
+ /**
* Appends LDC or LDC_W. The pushed item is a <code>String</code>
* object.
*
diff --git a/src/main/javassist/bytecode/ClassFile.java b/src/main/javassist/bytecode/ClassFile.java
index d07d108..5e7475a 100644
--- a/src/main/javassist/bytecode/ClassFile.java
+++ b/src/main/javassist/bytecode/ClassFile.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -19,16 +20,41 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
+
import javassist.CannotCompileException;
/**
* <code>ClassFile</code> represents a Java <code>.class</code> file, which
* consists of a constant pool, methods, fields, and attributes.
- *
+ *
+ * <p>For example,</p>
+ * <blockquote><pre>
+ * ClassFile cf = new ClassFile(false, "test.Foo", null);
+ * cf.setInterfaces(new String[] { "java.lang.Cloneable" });
+ *
+ * FieldInfo f = new FieldInfo(cf.getConstPool(), "width", "I");
+ * f.setAccessFlags(AccessFlag.PUBLIC);
+ * cf.addField(f);
+ *
+ * cf.write(new DataOutputStream(new FileOutputStream("Foo.class")));
+ * </pre></blockquote>
+ * <p>This code generates a class file <code>Foo.class</code> for the following class:</p>
+ * <blockquote><pre>
+ * package test;
+ * class Foo implements Cloneable {
+ * public int width;
+ * }
+ * </pre></blockquote>
+ *
+ * @see FieldInfo
+ * @see MethodInfo
+ * @see ClassFileWriter
* @see javassist.CtClass#getClassFile()
+ * @see javassist.ClassPool#makeClass(ClassFile)
*/
public final class ClassFile {
int major, minor; // version number
@@ -37,9 +63,9 @@ public final class ClassFile {
int accessFlags;
int superClass;
int[] interfaces;
- ArrayList fields;
- ArrayList methods;
- ArrayList attributes;
+ List<FieldInfo> fields;
+ List<MethodInfo> methods;
+ List<AttributeInfo> attributes;
String thisclassname; // not JVM-internal name
String[] cachedInterfaces;
String cachedSuperclass;
@@ -87,18 +113,69 @@ public final class ClassFile {
public static final int JAVA_7 = 51;
/**
- * The major version number of class files created
- * from scratch. The default value is 47 (JDK 1.3)
- * or 49 (JDK 1.5) if the JVM supports <code>java.lang.StringBuilder</code>.
+ * The major version number of class files
+ * for JDK 1.8.
+ */
+ public static final int JAVA_8 = 52;
+
+ /**
+ * The major version number of class files
+ * for JDK 1.9.
+ */
+ public static final int JAVA_9 = 53;
+
+ /**
+ * The major version number of class files
+ * for JDK 10.
*/
- public static int MAJOR_VERSION = JAVA_3;
+ public static final int JAVA_10 = 54;
+
+ /**
+ * The major version number of class files
+ * for JDK 11.
+ */
+ public static final int JAVA_11 = 55;
+
+ /**
+ * The major version number of class files created
+ * from scratch. The default value is 47 (JDK 1.3).
+ * It is 49 (JDK 1.5)
+ * if the JVM supports <code>java.lang.StringBuilder</code>.
+ * It is 50 (JDK 1.6)
+ * if the JVM supports <code>java.util.zip.DeflaterInputStream</code>.
+ * It is 51 (JDK 1.7)
+ * if the JVM supports <code>java.lang.invoke.CallSite</code>.
+ * It is 52 (JDK 1.8)
+ * if the JVM supports <code>java.util.function.Function</code>.
+ * It is 53 (JDK 1.9)
+ * if the JVM supports <code>java.lang.reflect.Module</code>.
+ * It is 54 (JDK 10)
+ * if the JVM supports <code>java.util.List.copyOf(Collection)</code>.
+ * It is 55 (JDK 11)
+ * if the JVM supports <code>java.util.Optional.isEmpty()</code>.
+ */
+ public static final int MAJOR_VERSION;
static {
+ int ver = JAVA_3;
try {
Class.forName("java.lang.StringBuilder");
- MAJOR_VERSION = JAVA_5;
+ ver = JAVA_5;
+ Class.forName("java.util.zip.DeflaterInputStream");
+ ver = JAVA_6;
+ Class.forName("java.lang.invoke.CallSite", false, ClassLoader.getSystemClassLoader());
+ ver = JAVA_7;
+ Class.forName("java.util.function.Function");
+ ver = JAVA_8;
+ Class.forName("java.lang.Module");
+ ver = JAVA_9;
+ List.class.getMethod("copyOf", Collection.class);
+ ver = JAVA_10;
+ Class.forName("java.util.Optional").getMethod("isEmpty");
+ ver = JAVA_11;
}
catch (Throwable t) {}
+ MAJOR_VERSION = ver;
}
/**
@@ -116,7 +193,7 @@ public final class ClassFile {
* @param classname
* a fully-qualified class name
* @param superclass
- * a fully-qualified super class name
+ * a fully-qualified super class name or null.
*/
public ClassFile(boolean isInterface, String classname, String superclass) {
major = MAJOR_VERSION;
@@ -130,11 +207,11 @@ public final class ClassFile {
initSuperclass(superclass);
interfaces = null;
- fields = new ArrayList();
- methods = new ArrayList();
+ fields = new ArrayList<FieldInfo>();
+ methods = new ArrayList<MethodInfo>();
thisclassname = classname;
- attributes = new ArrayList();
+ attributes = new ArrayList<AttributeInfo>();
attributes.add(new SourceFileAttribute(constPool,
getSourcefileName(thisclassname)));
}
@@ -151,11 +228,7 @@ public final class ClassFile {
}
private static String getSourcefileName(String qname) {
- int index = qname.lastIndexOf('.');
- if (index >= 0)
- qname = qname.substring(index + 1);
-
- return qname + ".java";
+ return qname.replaceAll("^.*\\.","") + ".java";
}
/**
@@ -165,19 +238,11 @@ public final class ClassFile {
*/
public void compact() {
ConstPool cp = compact0();
- ArrayList list = methods;
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ for (MethodInfo minfo:methods)
minfo.compact(cp);
- }
- list = fields;
- n = list.size();
- for (int i = 0; i < n; ++i) {
- FieldInfo finfo = (FieldInfo)list.get(i);
+ for (FieldInfo finfo:fields)
finfo.compact(cp);
- }
attributes = AttributeInfo.copyAll(attributes, cp);
constPool = cp;
@@ -190,12 +255,10 @@ public final class ClassFile {
if (sc != null)
superClass = cp.addClassInfo(getSuperclass());
- if (interfaces != null) {
- int n = interfaces.length;
- for (int i = 0; i < n; ++i)
+ if (interfaces != null)
+ for (int i = 0; i < interfaces.length; ++i)
interfaces[i]
= cp.addClassInfo(constPool.getClassInfo(interfaces[i]));
- }
return cp;
}
@@ -208,7 +271,7 @@ public final class ClassFile {
*/
public void prune() {
ConstPool cp = compact0();
- ArrayList newAttributes = new ArrayList();
+ List<AttributeInfo> newAttributes = new ArrayList<AttributeInfo>();
AttributeInfo invisibleAnnotations
= getAttribute(AnnotationsAttribute.invisibleTag);
if (invisibleAnnotations != null) {
@@ -223,26 +286,18 @@ public final class ClassFile {
newAttributes.add(visibleAnnotations);
}
- AttributeInfo signature
+ AttributeInfo signature
= getAttribute(SignatureAttribute.tag);
if (signature != null) {
signature = signature.copy(cp, null);
newAttributes.add(signature);
}
-
- ArrayList list = methods;
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+
+ for (MethodInfo minfo:methods)
minfo.prune(cp);
- }
- list = fields;
- n = list.size();
- for (int i = 0; i < n; ++i) {
- FieldInfo finfo = (FieldInfo)list.get(i);
+ for (FieldInfo finfo:fields)
finfo.prune(cp);
- }
attributes = newAttributes;
constPool = cp;
@@ -303,7 +358,7 @@ public final class ClassFile {
*
* <p>The returned value is obtained from <code>inner_class_access_flags</code>
* of the entry representing this nested class itself
- * in <code>InnerClasses_attribute</code>>.
+ * in <code>InnerClasses_attribute</code>.
*/
public int getInnerAccessFlags() {
InnerClassesAttribute ica
@@ -367,12 +422,8 @@ public final class ClassFile {
try {
this.superClass = constPool.addClassInfo(superclass);
- ArrayList list = methods;
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ for (MethodInfo minfo:methods)
minfo.setSuperclass(superclass);
- }
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
@@ -395,9 +446,6 @@ public final class ClassFile {
* the substituted class name
*/
public final void renameClass(String oldname, String newname) {
- ArrayList list;
- int n;
-
if (oldname.equals(newname))
return;
@@ -409,19 +457,13 @@ public final class ClassFile {
constPool.renameClass(oldname, newname);
AttributeInfo.renameClass(attributes, oldname, newname);
- list = methods;
- n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ for (MethodInfo minfo :methods) {
String desc = minfo.getDescriptor();
minfo.setDescriptor(Descriptor.rename(desc, oldname, newname));
AttributeInfo.renameClass(minfo.getAttributes(), oldname, newname);
}
- list = fields;
- n = list.size();
- for (int i = 0; i < n; ++i) {
- FieldInfo finfo = (FieldInfo)list.get(i);
+ for (FieldInfo finfo:fields) {
String desc = finfo.getDescriptor();
finfo.setDescriptor(Descriptor.rename(desc, oldname, newname));
AttributeInfo.renameClass(finfo.getAttributes(), oldname, newname);
@@ -437,8 +479,8 @@ public final class ClassFile {
* representation like <code>java/lang/Object</code>.
* @see #renameClass(String,String)
*/
- public final void renameClass(Map classnames) {
- String jvmNewThisName = (String)classnames.get(Descriptor
+ public final void renameClass(Map<String,String> classnames) {
+ String jvmNewThisName = classnames.get(Descriptor
.toJvmName(thisclassname));
if (jvmNewThisName != null)
thisclassname = Descriptor.toJavaName(jvmNewThisName);
@@ -446,19 +488,13 @@ public final class ClassFile {
constPool.renameClass(classnames);
AttributeInfo.renameClass(attributes, classnames);
- ArrayList list = methods;
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ for (MethodInfo minfo:methods) {
String desc = minfo.getDescriptor();
minfo.setDescriptor(Descriptor.rename(desc, classnames));
AttributeInfo.renameClass(minfo.getAttributes(), classnames);
}
- list = fields;
- n = list.size();
- for (int i = 0; i < n; ++i) {
- FieldInfo finfo = (FieldInfo)list.get(i);
+ for (FieldInfo finfo:fields) {
String desc = finfo.getDescriptor();
finfo.setDescriptor(Descriptor.rename(desc, classnames));
AttributeInfo.renameClass(finfo.getAttributes(), classnames);
@@ -469,23 +505,17 @@ public final class ClassFile {
* Internal-use only.
* <code>CtClass.getRefClasses()</code> calls this method.
*/
- public final void getRefClasses(Map classnames) {
+ public final void getRefClasses(Map<String,String> classnames) {
constPool.renameClass(classnames);
AttributeInfo.getRefClasses(attributes, classnames);
- ArrayList list = methods;
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ for (MethodInfo minfo:methods) {
String desc = minfo.getDescriptor();
Descriptor.rename(desc, classnames);
AttributeInfo.getRefClasses(minfo.getAttributes(), classnames);
}
- list = fields;
- n = list.size();
- for (int i = 0; i < n; ++i) {
- FieldInfo finfo = (FieldInfo)list.get(i);
+ for (FieldInfo finfo:fields) {
String desc = finfo.getDescriptor();
Descriptor.rename(desc, classnames);
AttributeInfo.getRefClasses(finfo.getAttributes(), classnames);
@@ -504,9 +534,8 @@ public final class ClassFile {
if (interfaces == null)
rtn = new String[0];
else {
- int n = interfaces.length;
- String[] list = new String[n];
- for (int i = 0; i < n; ++i)
+ String[] list = new String[interfaces.length];
+ for (int i = 0; i < interfaces.length; ++i)
list[i] = constPool.getClassInfo(interfaces[i]);
rtn = list;
@@ -525,9 +554,8 @@ public final class ClassFile {
public void setInterfaces(String[] nameList) {
cachedInterfaces = null;
if (nameList != null) {
- int n = nameList.length;
- interfaces = new int[n];
- for (int i = 0; i < n; ++i)
+ interfaces = new int[nameList.length];
+ for (int i = 0; i < nameList.length; ++i)
interfaces[i] = constPool.addClassInfo(nameList[i]);
}
}
@@ -557,7 +585,7 @@ public final class ClassFile {
* @return a list of <code>FieldInfo</code>.
* @see FieldInfo
*/
- public List getFields() {
+ public List<FieldInfo> getFields() {
return fields;
}
@@ -585,12 +613,9 @@ public final class ClassFile {
private void testExistingField(String name, String descriptor)
throws DuplicateMemberException {
- ListIterator it = fields.listIterator(0);
- while (it.hasNext()) {
- FieldInfo minfo = (FieldInfo)it.next();
+ for (FieldInfo minfo:fields)
if (minfo.getName().equals(name))
throw new DuplicateMemberException("duplicate field: " + name);
- }
}
/**
@@ -599,7 +624,7 @@ public final class ClassFile {
* @return a list of <code>MethodInfo</code>.
* @see MethodInfo
*/
- public List getMethods() {
+ public List<MethodInfo> getMethods() {
return methods;
}
@@ -610,14 +635,9 @@ public final class ClassFile {
* @return null if no such method is found.
*/
public MethodInfo getMethod(String name) {
- ArrayList list = methods;
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ for (MethodInfo minfo:methods)
if (minfo.getName().equals(name))
return minfo;
- }
-
return null;
}
@@ -658,16 +678,16 @@ public final class ClassFile {
{
String name = newMinfo.getName();
String descriptor = newMinfo.getDescriptor();
- ListIterator it = methods.listIterator(0);
+ ListIterator<MethodInfo> it = methods.listIterator(0);
while (it.hasNext())
- if (isDuplicated(newMinfo, name, descriptor, (MethodInfo)it.next(), it))
+ if (isDuplicated(newMinfo, name, descriptor, it.next(), it))
throw new DuplicateMemberException("duplicate method: " + name
+ " in " + this.getName());
}
private static boolean isDuplicated(MethodInfo newMethod, String newName,
String newDesc, MethodInfo minfo,
- ListIterator it)
+ ListIterator<MethodInfo> it)
{
if (!minfo.getName().equals(newName))
return false;
@@ -679,13 +699,13 @@ public final class ClassFile {
if (desc.equals(newDesc)) {
if (notBridgeMethod(minfo))
return true;
- else {
- it.remove();
- return false;
- }
+ // if the bridge method with the same signature
+ // already exists, replace it.
+ it.remove();
+ return false;
}
- else
- return notBridgeMethod(minfo) && notBridgeMethod(newMethod);
+ return false;
+ // return notBridgeMethod(minfo) && notBridgeMethod(newMethod);
}
/* For a bridge method, see Sec. 15.12.4.5 of JLS 3rd Ed.
@@ -704,7 +724,7 @@ public final class ClassFile {
* @return a list of <code>AttributeInfo</code> objects.
* @see AttributeInfo
*/
- public List getAttributes() {
+ public List<AttributeInfo> getAttributes() {
return attributes;
}
@@ -712,23 +732,34 @@ public final class ClassFile {
* Returns the attribute with the specified name. If there are multiple
* attributes with that name, this method returns either of them. It
* returns null if the specified attributed is not found.
- *
+ *
+ * <p>An attribute name can be obtained by, for example,
+ * {@link AnnotationsAttribute#visibleTag} or
+ * {@link AnnotationsAttribute#invisibleTag}.
+ * </p>
+ *
* @param name attribute name
* @see #getAttributes()
*/
public AttributeInfo getAttribute(String name) {
- ArrayList list = attributes;
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- AttributeInfo ai = (AttributeInfo)list.get(i);
+ for (AttributeInfo ai:attributes)
if (ai.getName().equals(name))
return ai;
- }
-
return null;
}
/**
+ * Removes an attribute with the specified name.
+ *
+ * @param name attribute name.
+ * @return the removed attribute or null.
+ * @since 3.21
+ */
+ public AttributeInfo removeAttribute(String name) {
+ return AttributeInfo.remove(attributes, name);
+ }
+
+ /**
* Appends an attribute. If there is already an attribute with the same
* name, the new one substitutes for it.
*
@@ -749,8 +780,7 @@ public final class ClassFile {
= (SourceFileAttribute)getAttribute(SourceFileAttribute.tag);
if (sf == null)
return null;
- else
- return sf.getFileName();
+ return sf.getFileName();
}
private void read(DataInputStream in) throws IOException {
@@ -777,16 +807,16 @@ public final class ClassFile {
ConstPool cp = constPool;
n = in.readUnsignedShort();
- fields = new ArrayList();
+ fields = new ArrayList<FieldInfo>();
for (i = 0; i < n; ++i)
addField2(new FieldInfo(cp, in));
n = in.readUnsignedShort();
- methods = new ArrayList();
+ methods = new ArrayList<MethodInfo>();
for (i = 0; i < n; ++i)
addMethod2(new MethodInfo(cp, in));
- attributes = new ArrayList();
+ attributes = new ArrayList<AttributeInfo>();
n = in.readUnsignedShort();
for (i = 0; i < n; ++i)
addAttribute(AttributeInfo.read(cp, in));
@@ -795,7 +825,7 @@ public final class ClassFile {
}
/**
- * Writes a class file represened by this object into an output stream.
+ * Writes a class file represented by this object into an output stream.
*/
public void write(DataOutputStream out) throws IOException {
int i, n;
@@ -817,21 +847,16 @@ public final class ClassFile {
for (i = 0; i < n; ++i)
out.writeShort(interfaces[i]);
- ArrayList list = fields;
- n = list.size();
+ n = fields.size();
out.writeShort(n);
for (i = 0; i < n; ++i) {
- FieldInfo finfo = (FieldInfo)list.get(i);
+ FieldInfo finfo = fields.get(i);
finfo.write(out);
}
- list = methods;
- n = list.size();
- out.writeShort(n);
- for (i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ out.writeShort(methods.size());
+ for (MethodInfo minfo:methods)
minfo.write(out);
- }
out.writeShort(attributes.size());
AttributeInfo.writeAll(attributes, out);
diff --git a/src/main/javassist/bytecode/ClassFilePrinter.java b/src/main/javassist/bytecode/ClassFilePrinter.java
index 08078dc..a9c7d9b 100644
--- a/src/main/javassist/bytecode/ClassFilePrinter.java
+++ b/src/main/javassist/bytecode/ClassFilePrinter.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,9 +17,10 @@
package javassist.bytecode;
import java.io.PrintWriter;
-import javassist.Modifier;
import java.util.List;
+import javassist.Modifier;
+
/**
* A utility class for priting the contents of a class file.
* It prints a constant pool table, fields, and methods in a
@@ -36,9 +38,6 @@ public class ClassFilePrinter {
* Prints the contents of a class file.
*/
public static void print(ClassFile cf, PrintWriter out) {
- List list;
- int n;
-
/* 0x0020 (SYNCHRONIZED) means ACC_SUPER if the modifiers
* are of a class.
*/
@@ -61,10 +60,8 @@ public class ClassFilePrinter {
}
out.println();
- list = cf.getFields();
- n = list.size();
- for (int i = 0; i < n; ++i) {
- FieldInfo finfo = (FieldInfo)list.get(i);
+ List<FieldInfo> fields = cf.getFields();
+ for (FieldInfo finfo:fields) {
int acc = finfo.getAccessFlags();
out.println(Modifier.toString(AccessFlag.toModifier(acc))
+ " " + finfo.getName() + "\t"
@@ -73,10 +70,8 @@ public class ClassFilePrinter {
}
out.println();
- list = cf.getMethods();
- n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
+ List<MethodInfo> methods = cf.getMethods();
+ for (MethodInfo minfo:methods) {
int acc = minfo.getAccessFlags();
out.println(Modifier.toString(AccessFlag.toModifier(acc))
+ " " + minfo.getName() + "\t"
@@ -89,13 +84,11 @@ public class ClassFilePrinter {
printAttributes(cf.getAttributes(), out, 'c');
}
- static void printAttributes(List list, PrintWriter out, char kind) {
+ static void printAttributes(List<AttributeInfo> list, PrintWriter out, char kind) {
if (list == null)
return;
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- AttributeInfo ai = (AttributeInfo)list.get(i);
+ for (AttributeInfo ai:list) {
if (ai instanceof CodeAttribute) {
CodeAttribute ca = (CodeAttribute)ai;
out.println("attribute: " + ai.getName() + ": "
diff --git a/src/main/javassist/bytecode/ClassFileWriter.java b/src/main/javassist/bytecode/ClassFileWriter.java
index bb7342a..931ffcb 100644
--- a/src/main/javassist/bytecode/ClassFileWriter.java
+++ b/src/main/javassist/bytecode/ClassFileWriter.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,10 +16,9 @@
package javassist.bytecode;
-import java.io.OutputStream;
import java.io.DataOutputStream;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
/**
* A quick class-file writer. This is useful when a generated
@@ -144,7 +144,7 @@ public class ClassFileWriter {
output.writeShort(fields.size());
fields.write(output);
- output.writeShort(methods.size());
+ output.writeShort(methods.numOfMethods());
methods.write(output);
}
catch (IOException e) {}
@@ -189,7 +189,7 @@ public class ClassFileWriter {
out.writeShort(fields.size());
fields.write(out);
- out.writeShort(methods.size());
+ out.writeShort(methods.numOfMethods());
methods.write(out);
if (aw == null)
out.writeShort(0);
@@ -502,7 +502,15 @@ public class ClassFileWriter {
output.writeInt(startPos + 2, output.getPos() - startPos - 6);
}
- int size() { return methodCount; }
+ /**
+ * Returns the length of the bytecode that has been added so far.
+ *
+ * @return the length in bytes.
+ * @since 3.19
+ */
+ public int size() { return output.getPos() - startPos - 14; }
+
+ int numOfMethods() { return methodCount; }
int dataSize() { return output.size(); }
@@ -642,6 +650,57 @@ public class ClassFileWriter {
}
/**
+ * Adds a new <code>CONSTANT_MethodHandle_info</code>
+ * structure.
+ *
+ * @param kind <code>reference_kind</code>
+ * such as {@link ConstPool#REF_invokeStatic <code>REF_invokeStatic</code>}.
+ * @param index <code>reference_index</code>.
+ * @return the index of the added entry.
+ *
+ * @since 3.17.1
+ */
+ public int addMethodHandleInfo(int kind, int index) {
+ output.write(MethodHandleInfo.tag);
+ output.write(kind);
+ output.writeShort(index);
+ return num++;
+ }
+
+ /**
+ * Adds a new <code>CONSTANT_MethodType_info</code>
+ * structure.
+ *
+ * @param desc <code>descriptor_index</code>.
+ * @return the index of the added entry.
+ *
+ * @since 3.17.1
+ */
+ public int addMethodTypeInfo(int desc) {
+ output.write(MethodTypeInfo.tag);
+ output.writeShort(desc);
+ return num++;
+ }
+
+ /**
+ * Adds a new <code>CONSTANT_InvokeDynamic_info</code>
+ * structure.
+ *
+ * @param bootstrap <code>bootstrap_method_attr_index</code>.
+ * @param nameAndTypeInfo <code>name_and_type_index</code>.
+ * @return the index of the added entry.
+ *
+ * @since 3.17.1
+ */
+ public int addInvokeDynamicInfo(int bootstrap,
+ int nameAndTypeInfo) {
+ output.write(InvokeDynamicInfo.tag);
+ output.writeShort(bootstrap);
+ output.writeShort(nameAndTypeInfo);
+ return num++;
+ }
+
+ /**
* Adds a new <code>CONSTANT_String_info</code>
* structure.
*
diff --git a/src/main/javassist/bytecode/CodeAnalyzer.java b/src/main/javassist/bytecode/CodeAnalyzer.java
index a078e1f..d02fb19 100644
--- a/src/main/javassist/bytecode/CodeAnalyzer.java
+++ b/src/main/javassist/bytecode/CodeAnalyzer.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -239,6 +240,10 @@ class CodeAnalyzer implements Opcode {
ci.u16bitAt(index + 1));
stack += Descriptor.dataSize(desc) - 1;
break;
+ case INVOKEDYNAMIC :
+ desc = constPool.getInvokeDynamicType(ci.u16bitAt(index + 1));
+ stack += Descriptor.dataSize(desc); // assume CosntPool#REF_invokeStatic
+ break;
case ATHROW :
stack = 1; // the stack becomes empty (1 means no values).
break;
diff --git a/src/main/javassist/bytecode/CodeAttribute.java b/src/main/javassist/bytecode/CodeAttribute.java
index 99dca1d..4c8ea2f 100644
--- a/src/main/javassist/bytecode/CodeAttribute.java
+++ b/src/main/javassist/bytecode/CodeAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -18,9 +19,8 @@ package javassist.bytecode;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
-import java.util.List;
import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.List;
import java.util.Map;
/**
@@ -31,6 +31,7 @@ import java.util.Map;
* use <code>CodeIterator</code>.
*
* @see CodeIterator
+ * @see #iterator()
*/
public class CodeAttribute extends AttributeInfo implements Opcode {
/**
@@ -43,7 +44,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
private int maxStack;
private int maxLocals;
private ExceptionTable exceptions;
- private ArrayList attributes;
+ private List<AttributeInfo> attributes;
/**
* Constructs a <code>Code_attribute</code>.
@@ -62,7 +63,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
maxLocals = locals;
info = code;
exceptions = etable;
- attributes = new ArrayList();
+ attributes = new ArrayList<AttributeInfo>();
}
/**
@@ -74,7 +75,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
* @param classnames pairs of replaced and substituted
* class names.
*/
- private CodeAttribute(ConstPool cp, CodeAttribute src, Map classnames)
+ private CodeAttribute(ConstPool cp, CodeAttribute src, Map<String,String> classnames)
throws BadBytecode
{
super(cp, tag);
@@ -82,11 +83,11 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
maxStack = src.getMaxStack();
maxLocals = src.getMaxLocals();
exceptions = src.getExceptionTable().copy(cp, classnames);
- attributes = new ArrayList();
- List src_attr = src.getAttributes();
+ attributes = new ArrayList<AttributeInfo>();
+ List<AttributeInfo> src_attr = src.getAttributes();
int num = src_attr.size();
for (int i = 0; i < num; ++i) {
- AttributeInfo ai = (AttributeInfo)src_attr.get(i);
+ AttributeInfo ai = src_attr.get(i);
attributes.add(ai.copy(cp, classnames));
}
@@ -97,6 +98,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
throws IOException
{
super(cp, name_id, (byte[])null);
+ @SuppressWarnings("unused")
int attr_len = in.readInt();
maxStack = in.readUnsignedShort();
@@ -108,7 +110,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
exceptions = new ExceptionTable(cp, in);
- attributes = new ArrayList();
+ attributes = new ArrayList<AttributeInfo>();
int num = in.readUnsignedShort();
for (int i = 0; i < num; ++i)
attributes.add(AttributeInfo.read(cp, in));
@@ -128,7 +130,8 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
*
* @return <code>CodeAttribute</code> object.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames)
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames)
throws RuntimeCopyException
{
try {
@@ -144,6 +147,9 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
* in <code>CodeAttribute</code>.
*/
public static class RuntimeCopyException extends RuntimeException {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
/**
* Constructs an exception.
*/
@@ -157,11 +163,13 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
* structure.
* The returned value is <code>attribute_length + 6</code>.
*/
+ @Override
public int length() {
return 18 + info.length + exceptions.size() * 8
+ AttributeInfo.getLength(attributes);
}
+ @Override
void write(DataOutputStream out) throws IOException {
out.writeShort(name); // attribute_name_index
out.writeInt(length() - 6); // attribute_length
@@ -179,7 +187,8 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
*
* @throws java.lang.UnsupportedOperationException always thrown.
*/
- public byte[] get() {
+ @Override
+ public byte[] get() {
throw new UnsupportedOperationException("CodeAttribute.get()");
}
@@ -188,19 +197,23 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
*
* @throws java.lang.UnsupportedOperationException always thrown.
*/
+ @Override
public void set(byte[] newinfo) {
throw new UnsupportedOperationException("CodeAttribute.set()");
}
+ @Override
void renameClass(String oldname, String newname) {
AttributeInfo.renameClass(attributes, oldname, newname);
}
- void renameClass(Map classnames) {
+ @Override
+ void renameClass(Map<String,String> classnames) {
AttributeInfo.renameClass(attributes, classnames);
}
- void getRefClasses(Map classnames) {
+ @Override
+ void getRefClasses(Map<String,String> classnames) {
AttributeInfo.getRefClasses(attributes, classnames);
}
@@ -292,7 +305,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
*
* @see AttributeInfo
*/
- public List getAttributes() { return attributes; }
+ public List<AttributeInfo> getAttributes() { return attributes; }
/**
* Returns the attribute with the specified name.
@@ -337,7 +350,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
/**
* Copies code.
*/
- private byte[] copyCode(ConstPool destCp, Map classnames,
+ private byte[] copyCode(ConstPool destCp, Map<String,String> classnames,
ExceptionTable etable, CodeAttribute destCa)
throws BadBytecode
{
@@ -351,7 +364,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
private static LdcEntry copyCode(byte[] code, int beginPos, int endPos,
ConstPool srcCp, byte[] newcode,
- ConstPool destCp, Map classnameMap)
+ ConstPool destCp, Map<String,String> classnameMap)
throws BadBytecode
{
int i2, index;
@@ -399,6 +412,12 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
newcode[i + 3] = code[i + 3];
newcode[i + 4] = code[i + 4];
break;
+ case INVOKEDYNAMIC :
+ copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
+ classnameMap);
+ newcode[i + 3] = 0;
+ newcode[i + 4] = 0;
+ break;
case MULTIANEWARRAY :
copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
classnameMap);
@@ -417,7 +436,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
private static void copyConstPoolInfo(int i, byte[] code, ConstPool srcCp,
byte[] newcode, ConstPool destCp,
- Map classnameMap) {
+ Map<String,String> classnameMap) {
int index = ((code[i] & 0xff) << 8) | (code[i + 1] & 0xff);
index = srcCp.copy(index, destCp, classnameMap);
newcode[i] = (byte)(index >> 8);
@@ -457,6 +476,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
* Changes the index numbers of the local variables
* to append a new parameter.
* This method does not update <code>LocalVariableAttribute</code>,
+ * <code>LocalVariableTypeAttribute</code>,
* <code>StackMapTable</code>, or <code>StackMap</code>.
* These attributes must be explicitly updated.
*
@@ -464,6 +484,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
* @param size the type size of the new parameter (1 or 2).
*
* @see LocalVariableAttribute#shiftIndex(int, int)
+ * @see LocalVariableTypeAttribute#shiftIndex(int, int)
* @see StackMapTable#insertLocal(int, int, int)
* @see StackMap#insertLocal(int, int, int)
*/
diff --git a/src/main/javassist/bytecode/CodeIterator.java b/src/main/javassist/bytecode/CodeIterator.java
index 7781b70..b9a23c2 100644
--- a/src/main/javassist/bytecode/CodeIterator.java
+++ b/src/main/javassist/bytecode/CodeIterator.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,10 +17,25 @@
package javassist.bytecode;
import java.util.ArrayList;
+import java.util.List;
/**
* An iterator for editing a code attribute.
*
+ * <p>To directly read or edit a bytecode sequence, call {@link #byteAt(int)}, {@link #s16bitAt(int)},
+ * {@link #writeByte(int, int)}, {@link #write16bit(int, int)}, and other methods.
+ * For example, if <code>method</code> refers to a <code>CtMethod</code> object,
+ * the following code substitutes the <code>NOP</code> instruction for the first
+ * instruction of the method:
+ *
+ * <pre>
+ * CodeAttribute ca = method.getMethodInfo().getCodeAttribute();
+ * CodeIterator ci = ca.iterator();
+ * ci.writeByte(Opcode.NOP, 0);</pre>
+ *
+ * <p>To visit every instruction, call {@link #next()} on a <code>CodeIterator</code>.
+ * It returns the index of the first byte of the next instruction.
+ *
* <p>If there are multiple <code>CodeIterator</code>s referring to the
* same <code>Code_attribute</code>, then inserting a gap by one
* <code>CodeIterator</code> will break the other
@@ -112,6 +128,11 @@ public class CodeIterator implements Opcode {
public int byteAt(int index) { return bytecode[index] & 0xff; }
/**
+ * Returns the signed 8bit value at the given index.
+ */
+ public int signedByteAt(int index) { return bytecode[index]; }
+
+ /**
* Writes an 8bit value at the given index.
*/
public void writeByte(int value, int index) {
@@ -285,8 +306,8 @@ public class CodeIterator implements Opcode {
String cname = cp.getMethodrefClassName(mref);
if (cname.equals(thisClassName) == (skipThis > 0))
return index;
- else
- break;
+
+ break;
}
}
}
@@ -724,10 +745,10 @@ public class CodeIterator implements Opcode {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3,
- 3, 3, 3, 3, 3, 5, 0, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3,
+ 3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3,
5, 5
};
- // 0 .. UNUSED (186), LOOKUPSWITCH, TABLESWITCH, WIDE
+ // 0 .. LOOKUPSWITCH, TABLESWITCH, WIDE
/**
* Calculates the index of the next opcode.
@@ -752,19 +773,15 @@ public class CodeIterator implements Opcode {
return index + 6;
else
return index + 4; // WIDE ...
- else {
- int index2 = (index & ~3) + 8;
- if (opcode == LOOKUPSWITCH) {
- int npairs = ByteArray.read32bit(code, index2);
- return index2 + npairs * 8 + 4;
- }
- else if (opcode == TABLESWITCH) {
- int low = ByteArray.read32bit(code, index2);
- int high = ByteArray.read32bit(code, index2 + 4);
- return index2 + (high - low + 1) * 4 + 8;
- }
- // else
- // throw new BadBytecode(opcode);
+ int index2 = (index & ~3) + 8;
+ if (opcode == LOOKUPSWITCH) {
+ int npairs = ByteArray.read32bit(code, index2);
+ return index2 + npairs * 8 + 4;
+ }
+ else if (opcode == TABLESWITCH) {
+ int low = ByteArray.read32bit(code, index2);
+ int high = ByteArray.read32bit(code, index2 + 4);
+ return index2 + (high - low + 1) * 4 + 8;
}
}
catch (IndexOutOfBoundsException e) {
@@ -776,7 +793,10 @@ public class CodeIterator implements Opcode {
// methods for implementing insertGap().
- static class AlignmentException extends Exception {}
+ static class AlignmentException extends Exception {
+
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;}
/**
* insertGapCore0() inserts a gap (some NOPs).
@@ -1037,6 +1057,14 @@ public class CodeIterator implements Opcode {
if (stack2 != null)
stack2.shiftPc(where, gapLength, exclusive);
}
+
+ void shiftForSwitch(int where, int gapLength) throws BadBytecode {
+ if (stack != null)
+ stack.shiftForSwitch(where, gapLength);
+
+ if (stack2 != null)
+ stack2.shiftForSwitch(where, gapLength);
+ }
}
/*
@@ -1046,23 +1074,23 @@ public class CodeIterator implements Opcode {
CodeAttribute ca, CodeAttribute.LdcEntry ldcs)
throws BadBytecode
{
- ArrayList jumps = makeJumpList(code, code.length);
+ Pointers pointers = new Pointers(0, 0, 0, etable, ca);
+ List<Branch> jumps = makeJumpList(code, code.length, pointers);
while (ldcs != null) {
addLdcW(ldcs, jumps);
ldcs = ldcs.next;
}
- Pointers pointers = new Pointers(0, 0, 0, etable, ca);
byte[] r = insertGap2w(code, 0, 0, false, jumps, pointers);
return r;
}
- private static void addLdcW(CodeAttribute.LdcEntry ldcs, ArrayList jumps) {
+ private static void addLdcW(CodeAttribute.LdcEntry ldcs, List<Branch> jumps) {
int where = ldcs.where;
LdcW ldcw = new LdcW(where, ldcs.index);
int s = jumps.size();
for (int i = 0; i < s; i++)
- if (where < ((Branch)jumps.get(i)).orgPos) {
+ if (where < jumps.get(i).orgPos) {
jumps.add(i, ldcw);
return;
}
@@ -1090,8 +1118,8 @@ public class CodeIterator implements Opcode {
if (gapLength <= 0)
return code;
- ArrayList jumps = makeJumpList(code, code.length);
Pointers pointers = new Pointers(currentPos, mark, where, etable, ca);
+ List<Branch> jumps = makeJumpList(code, code.length, pointers);
byte[] r = insertGap2w(code, where, gapLength, exclusive, jumps, pointers);
currentPos = pointers.cursor;
mark = pointers.mark;
@@ -1108,42 +1136,39 @@ public class CodeIterator implements Opcode {
}
private static byte[] insertGap2w(byte[] code, int where, int gapLength,
- boolean exclusive, ArrayList jumps, Pointers ptrs)
+ boolean exclusive, List<Branch> jumps, Pointers ptrs)
throws BadBytecode
{
- int n = jumps.size();
if (gapLength > 0) {
ptrs.shiftPc(where, gapLength, exclusive);
- for (int i = 0; i < n; i++)
- ((Branch)jumps.get(i)).shift(where, gapLength, exclusive);
+ for (Branch b:jumps)
+ b.shift(where, gapLength, exclusive);
}
boolean unstable = true;
do {
while (unstable) {
unstable = false;
- for (int i = 0; i < n; i++) {
- Branch b = (Branch)jumps.get(i);
+ for (Branch b:jumps) {
if (b.expanded()) {
unstable = true;
int p = b.pos;
int delta = b.deltaSize();
ptrs.shiftPc(p, delta, false);
- for (int j = 0; j < n; j++)
- ((Branch)jumps.get(j)).shift(p, delta, false);
+ for (Branch bb:jumps)
+ bb.shift(p, delta, false);
}
}
}
- for (int i = 0; i < n; i++) {
- Branch b = (Branch)jumps.get(i);
+ for (Branch b:jumps) {
int diff = b.gapChanged();
if (diff > 0) {
unstable = true;
int p = b.pos;
ptrs.shiftPc(p, diff, false);
- for (int j = 0; j < n; j++)
- ((Branch)jumps.get(j)).shift(p, diff, false);
+ for (Branch bb:jumps)
+ bb.shift(p, diff, false);
}
}
} while (unstable);
@@ -1151,10 +1176,10 @@ public class CodeIterator implements Opcode {
return makeExapndedCode(code, jumps, where, gapLength);
}
- private static ArrayList makeJumpList(byte[] code, int endPos)
+ private static List<Branch> makeJumpList(byte[] code, int endPos, Pointers ptrs)
throws BadBytecode
{
- ArrayList jumps = new ArrayList();
+ List<Branch> jumps = new ArrayList<Branch>();
int nextPos;
for (int i = 0; i < endPos; i = nextPos) {
nextPos = nextOpcode(code, i);
@@ -1190,7 +1215,7 @@ public class CodeIterator implements Opcode {
i0 += 4;
}
- jumps.add(new Table(i, defaultbyte, lowbyte, highbyte, offsets));
+ jumps.add(new Table(i, defaultbyte, lowbyte, highbyte, offsets, ptrs));
}
else if (inst == LOOKUPSWITCH) {
int i2 = (i & ~3) + 4; // 0-3 byte padding
@@ -1205,23 +1230,21 @@ public class CodeIterator implements Opcode {
i0 += 8;
}
- jumps.add(new Lookup(i, defaultbyte, matches, offsets));
+ jumps.add(new Lookup(i, defaultbyte, matches, offsets, ptrs));
}
}
return jumps;
}
- private static byte[] makeExapndedCode(byte[] code, ArrayList jumps,
+ private static byte[] makeExapndedCode(byte[] code, List<Branch> jumps,
int where, int gapLength)
throws BadBytecode
{
int n = jumps.size();
int size = code.length + gapLength;
- for (int i = 0; i < n; i++) {
- Branch b = (Branch)jumps.get(i);
+ for (Branch b:jumps)
size += b.deltaSize();
- }
byte[] newcode = new byte[size];
int src = 0, dest = 0, bindex = 0;
@@ -1229,12 +1252,12 @@ public class CodeIterator implements Opcode {
Branch b;
int bpos;
if (0 < n) {
- b = (Branch)jumps.get(0);
+ b = jumps.get(0);
bpos = b.orgPos;
}
else {
b = null;
- bpos = len; // src will be never equal to bpos
+ bpos = len; // src will be never equal to bpos
}
while (src < len) {
@@ -1251,7 +1274,7 @@ public class CodeIterator implements Opcode {
src += s;
dest += s + b.deltaSize();
if (++bindex < n) {
- b = (Branch)jumps.get(bindex);
+ b = jumps.get(bindex);
bpos = b.orgPos;
}
else {
@@ -1299,7 +1322,7 @@ public class CodeIterator implements Opcode {
int deltaSize() { return 0; } // newSize - oldSize
// This returns the original instruction size.
- abstract int write(int srcPos, byte[] code, int destPos, byte[] newcode);
+ abstract int write(int srcPos, byte[] code, int destPos, byte[] newcode) throws BadBytecode;
}
/* used by changeLdcToLdcW() and CodeAttribute.LdcEntry.
@@ -1313,17 +1336,19 @@ public class CodeIterator implements Opcode {
state = true;
}
+ @Override
boolean expanded() {
if (state) {
state = false;
return true;
}
- else
- return false;
+ return false;
}
+ @Override
int deltaSize() { return 1; }
+ @Override
int write(int srcPos, byte[] code, int destPos, byte[] newcode) {
newcode[destPos] = LDC_W;
ByteArray.write16bit(index, newcode, destPos + 1);
@@ -1344,6 +1369,7 @@ public class CodeIterator implements Opcode {
state = BIT16;
}
+ @Override
void shift(int where, int gapLength, boolean exclusive) {
offset = shiftOffset(pos, offset, where, gapLength, exclusive);
super.shift(where, gapLength, exclusive);
@@ -1352,18 +1378,20 @@ public class CodeIterator implements Opcode {
state = EXPAND;
}
+ @Override
boolean expanded() {
if (state == EXPAND) {
state = BIT32;
return true;
}
- else
- return false;
+ return false;
}
+ @Override
abstract int deltaSize();
abstract void write32(int src, byte[] code, int dest, byte[] newcode);
+ @Override
int write(int src, byte[] code, int dest, byte[] newcode) {
if (state == BIT32)
write32(src, code, dest, newcode);
@@ -1382,10 +1410,12 @@ public class CodeIterator implements Opcode {
super(p, off);
}
+ @Override
int deltaSize() {
return state == BIT32 ? 2 : 0;
}
+ @Override
void write32(int src, byte[] code, int dest, byte[] newcode) {
newcode[dest] = (byte)(((code[src] & 0xff) == GOTO) ? GOTO_W : JSR_W);
ByteArray.write32bit(offset, newcode, dest + 1);
@@ -1398,10 +1428,12 @@ public class CodeIterator implements Opcode {
super(p, off);
}
+ @Override
int deltaSize() {
return state == BIT32 ? 5 : 0;
}
+ @Override
void write32(int src, byte[] code, int dest, byte[] newcode) {
newcode[dest] = (byte)opcode(code[src] & 0xff);
newcode[dest + 1] = 0;
@@ -1415,12 +1447,9 @@ public class CodeIterator implements Opcode {
return IFNONNULL;
else if (op == IFNONNULL)
return IFNULL;
- else {
- if (((op - IFEQ) & 1) == 0)
- return op + 1;
- else
- return op - 1;
- }
+ if (((op - IFEQ) & 1) == 0)
+ return op + 1;
+ return op - 1;
}
}
@@ -1432,11 +1461,13 @@ public class CodeIterator implements Opcode {
offset = off;
}
+ @Override
void shift(int where, int gapLength, boolean exclusive) {
offset = shiftOffset(pos, offset, where, gapLength, exclusive);
super.shift(where, gapLength, exclusive);
}
+ @Override
int write(int src, byte[] code, int dest, byte[] newcode) {
newcode[dest] = code[src];
ByteArray.write32bit(offset, newcode, dest + 1);
@@ -1447,14 +1478,17 @@ public class CodeIterator implements Opcode {
static abstract class Switcher extends Branch {
int gap, defaultByte;
int[] offsets;
+ Pointers pointers;
- Switcher(int pos, int defaultByte, int[] offsets) {
+ Switcher(int pos, int defaultByte, int[] offsets, Pointers ptrs) {
super(pos);
this.gap = 3 - (pos & 3);
this.defaultByte = defaultByte;
this.offsets = offsets;
+ this.pointers = ptrs;
}
+ @Override
void shift(int where, int gapLength, boolean exclusive) {
int p = pos;
defaultByte = shiftOffset(p, defaultByte, where, gapLength, exclusive);
@@ -1465,6 +1499,7 @@ public class CodeIterator implements Opcode {
super.shift(where, gapLength, exclusive);
}
+ @Override
int gapChanged() {
int newGap = 3 - (pos & 3);
if (newGap > gap) {
@@ -1476,15 +1511,19 @@ public class CodeIterator implements Opcode {
return 0;
}
+ @Override
int deltaSize() {
return gap - (3 - (orgPos & 3));
}
- int write(int src, byte[] code, int dest, byte[] newcode) {
+ @Override
+ int write(int src, byte[] code, int dest, byte[] newcode) throws BadBytecode {
int padding = 3 - (pos & 3);
int nops = gap - padding;
int bytecodeSize = 5 + (3 - (orgPos & 3)) + tableSize();
- adjustOffsets(bytecodeSize, nops);
+ if (nops > 0)
+ adjustOffsets(bytecodeSize, nops);
+
newcode[dest++] = code[src];
while (padding-- > 0)
newcode[dest++] = 0;
@@ -1510,7 +1549,8 @@ public class CodeIterator implements Opcode {
* dead code. It complicates the generation of StackMap and
* StackMapTable.
*/
- void adjustOffsets(int size, int nops) {
+ void adjustOffsets(int size, int nops) throws BadBytecode {
+ pointers.shiftForSwitch(pos + size, nops);
if (defaultByte == size)
defaultByte -= nops;
@@ -1523,12 +1563,13 @@ public class CodeIterator implements Opcode {
static class Table extends Switcher {
int low, high;
- Table(int pos, int defaultByte, int low, int high, int[] offsets) {
- super(pos, defaultByte, offsets);
+ Table(int pos, int defaultByte, int low, int high, int[] offsets, Pointers ptrs) {
+ super(pos, defaultByte, offsets, ptrs);
this.low = low;
this.high = high;
}
+ @Override
int write2(int dest, byte[] newcode) {
ByteArray.write32bit(low, newcode, dest);
ByteArray.write32bit(high, newcode, dest + 4);
@@ -1542,17 +1583,19 @@ public class CodeIterator implements Opcode {
return 8 + 4 * n;
}
+ @Override
int tableSize() { return 8 + 4 * offsets.length; }
}
static class Lookup extends Switcher {
int[] matches;
- Lookup(int pos, int defaultByte, int[] matches, int[] offsets) {
- super(pos, defaultByte, offsets);
+ Lookup(int pos, int defaultByte, int[] matches, int[] offsets, Pointers ptrs) {
+ super(pos, defaultByte, offsets, ptrs);
this.matches = matches;
}
+ @Override
int write2(int dest, byte[] newcode) {
int n = matches.length;
ByteArray.write32bit(n, newcode, dest);
@@ -1566,6 +1609,7 @@ public class CodeIterator implements Opcode {
return 4 + 8 * n;
}
+ @Override
int tableSize() { return 4 + 8 * matches.length; }
}
}
diff --git a/src/main/javassist/bytecode/ConstPool.java b/src/main/javassist/bytecode/ConstPool.java
index df4e542..0447ece 100644
--- a/src/main/javassist/bytecode/ConstPool.java
+++ b/src/main/javassist/bytecode/ConstPool.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,11 @@
package javassist.bytecode;
+import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintWriter;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -30,30 +31,12 @@ import javassist.CtClass;
/**
* Constant pool table.
*/
-public final class ConstPool {
+public final class ConstPool
+{
LongVector items;
int numOfItems;
- HashMap classes;
- HashMap strings;
- ConstInfo[] constInfoCache;
- int[] constInfoIndexCache;
int thisClassInfo;
-
- private static final int CACHE_SIZE = 32;
-
- /**
- * A hash function for CACHE_SIZE
- */
- private static int hashFunc(int a, int b) {
- int h = -2128831035;
- final int prime = 16777619;
- h = (h ^ (a & 0xff)) * prime;
- h = (h ^ (b & 0xff)) * prime;
-
- // changing the hash key size from 32bit to 5bit
- h = (h >> 5) ^ (h & 0x1f);
- return h & 0x1f; // 0..31
- }
+ Map<ConstInfo,ConstInfo> itemsCache;
/**
* <code>CONSTANT_Class</code>
@@ -112,24 +95,92 @@ public final class ConstPool {
public static final int CONST_Utf8 = Utf8Info.tag;
/**
+ * <code>CONSTANT_MethodHandle</code>
+ */
+ public static final int CONST_MethodHandle = MethodHandleInfo.tag;
+
+ /**
+ * <code>CONSTANT_MethodHandle</code>
+ */
+ public static final int CONST_MethodType = MethodTypeInfo.tag;
+
+ /**
+ * <code>CONSTANT_MethodHandle</code>
+ */
+ public static final int CONST_InvokeDynamic = InvokeDynamicInfo.tag;
+
+ /**
+ * <code>CONSTANT_Module</code>
+ */
+ public static final int CONST_Module = ModuleInfo.tag;
+
+ /**
+ * <code>CONSTANT_Package</code>
+ */
+ public static final int CONST_Package = PackageInfo.tag;
+
+ /**
* Represents the class using this constant pool table.
*/
public static final CtClass THIS = null;
/**
+ * <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>.
+ */
+ public static final int REF_getField = 1;
+
+ /**
+ * <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>.
+ */
+ public static final int REF_getStatic = 2;
+
+ /**
+ * <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>.
+ */
+ public static final int REF_putField = 3;
+
+ /**
+ * <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>.
+ */
+ public static final int REF_putStatic = 4;
+
+ /**
+ * <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>.
+ */
+ public static final int REF_invokeVirtual = 5;
+
+ /**
+ * <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>.
+ */
+ public static final int REF_invokeStatic = 6;
+
+ /**
+ * <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>.
+ */
+ public static final int REF_invokeSpecial = 7;
+
+ /**
+ * <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>.
+ */
+ public static final int REF_newInvokeSpecial = 8;
+
+ /**
+ * <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>.
+ */
+ public static final int REF_invokeInterface = 9;
+
+ /**
* Constructs a constant pool table.
*
* @param thisclass the name of the class using this constant
* pool table
*/
- public ConstPool(String thisclass) {
+ public ConstPool(String thisclass)
+ {
items = new LongVector();
+ itemsCache = null;
numOfItems = 0;
- addItem(null); // index 0 is reserved by the JVM.
- classes = new HashMap();
- strings = new HashMap();
- constInfoCache = new ConstInfo[CACHE_SIZE];
- constInfoIndexCache = new int[CACHE_SIZE];
+ addItem0(null); // index 0 is reserved by the JVM.
thisClassInfo = addClassInfo(thisclass);
}
@@ -138,35 +189,33 @@ public final class ConstPool {
*
* @param in byte stream.
*/
- public ConstPool(DataInputStream in) throws IOException {
- classes = new HashMap();
- strings = new HashMap();
- constInfoCache = new ConstInfo[CACHE_SIZE];
- constInfoIndexCache = new int[CACHE_SIZE];
+ public ConstPool(DataInputStream in) throws IOException
+ {
+ itemsCache = null;
thisClassInfo = 0;
/* read() initializes items and numOfItems, and do addItem(null).
*/
read(in);
}
- void prune() {
- classes = new HashMap();
- strings = new HashMap();
- constInfoCache = new ConstInfo[CACHE_SIZE];
- constInfoIndexCache = new int[CACHE_SIZE];
+ void prune()
+ {
+ itemsCache = null;
}
/**
* Returns the number of entries in this table.
*/
- public int getSize() {
+ public int getSize()
+ {
return numOfItems;
}
/**
* Returns the name of the class using this constant pool table.
*/
- public String getClassName() {
+ public String getClassName()
+ {
return getClassInfo(thisClassInfo);
}
@@ -174,23 +223,30 @@ public final class ConstPool {
* Returns the index of <code>CONSTANT_Class_info</code> structure
* specifying the class using this constant pool table.
*/
- public int getThisClassInfo() {
+ public int getThisClassInfo()
+ {
return thisClassInfo;
}
- void setThisClassInfo(int i) {
+ void setThisClassInfo(int i)
+ {
thisClassInfo = i;
}
- ConstInfo getItem(int n) {
+ ConstInfo getItem(int n)
+ {
return items.elementAt(n);
}
/**
* Returns the <code>tag</code> field of the constant pool table
* entry at the given index.
+ *
+ * @return either <code>CONST_Class</code>, <code>CONST_Fieldref</code>,
+ * <code>CONST_Methodref</code>, or ...
*/
- public int getTag(int index) {
+ public int getTag(int index)
+ {
return getItem(index).getTag();
}
@@ -201,16 +257,36 @@ public final class ConstPool {
* @return a fully-qualified class or interface name specified
* by <code>name_index</code>. If the type is an array
* type, this method returns an encoded name like
- * <code>[java.lang.Object;</code> (note that the separators
+ * <code>[Ljava.lang.Object;</code> (note that the separators
* are not slashes but dots).
* @see javassist.ClassPool#getCtClass(String)
*/
- public String getClassInfo(int index) {
+ public String getClassInfo(int index)
+ {
ClassInfo c = (ClassInfo)getItem(index);
if (c == null)
return null;
- else
- return Descriptor.toJavaName(getUtf8Info(c.name));
+ return Descriptor.toJavaName(getUtf8Info(c.name));
+ }
+
+ /**
+ * Reads <code>CONSTANT_Class_info</code> structure
+ * at the given index.
+ *
+ * @return the descriptor of the type specified
+ * by <code>name_index</code>.
+ * @see javassist.ClassPool#getCtClass(String)
+ * @since 3.15
+ */
+ public String getClassInfoByDescriptor(int index)
+ {
+ ClassInfo c = (ClassInfo)getItem(index);
+ if (c == null)
+ return null;
+ String className = getUtf8Info(c.name);
+ if (className.charAt(0) == '[')
+ return className;
+ return Descriptor.of(className);
}
/**
@@ -218,7 +294,8 @@ public final class ConstPool {
* <code>CONSTANT_NameAndType_info</code> structure
* at the given index.
*/
- public int getNameAndTypeName(int index) {
+ public int getNameAndTypeName(int index)
+ {
NameAndTypeInfo ntinfo = (NameAndTypeInfo)getItem(index);
return ntinfo.memberName;
}
@@ -228,7 +305,8 @@ public final class ConstPool {
* <code>CONSTANT_NameAndType_info</code> structure
* at the given index.
*/
- public int getNameAndTypeDescriptor(int index) {
+ public int getNameAndTypeDescriptor(int index)
+ {
NameAndTypeInfo ntinfo = (NameAndTypeInfo)getItem(index);
return ntinfo.typeDescriptor;
}
@@ -242,7 +320,8 @@ public final class ConstPool {
*
* @since 3.6
*/
- public int getMemberClass(int index) {
+ public int getMemberClass(int index)
+ {
MemberrefInfo minfo = (MemberrefInfo)getItem(index);
return minfo.classIndex;
}
@@ -256,7 +335,8 @@ public final class ConstPool {
*
* @since 3.6
*/
- public int getMemberNameAndType(int index) {
+ public int getMemberNameAndType(int index)
+ {
MemberrefInfo minfo = (MemberrefInfo)getItem(index);
return minfo.nameAndTypeIndex;
}
@@ -266,7 +346,8 @@ public final class ConstPool {
* <code>CONSTANT_Fieldref_info</code> structure
* at the given index.
*/
- public int getFieldrefClass(int index) {
+ public int getFieldrefClass(int index)
+ {
FieldrefInfo finfo = (FieldrefInfo)getItem(index);
return finfo.classIndex;
}
@@ -278,12 +359,12 @@ public final class ConstPool {
*
* @return the name of the class at that <code>class_index</code>.
*/
- public String getFieldrefClassName(int index) {
+ public String getFieldrefClassName(int index)
+ {
FieldrefInfo f = (FieldrefInfo)getItem(index);
if (f == null)
return null;
- else
- return getClassInfo(f.classIndex);
+ return getClassInfo(f.classIndex);
}
/**
@@ -291,7 +372,8 @@ public final class ConstPool {
* <code>CONSTANT_Fieldref_info</code> structure
* at the given index.
*/
- public int getFieldrefNameAndType(int index) {
+ public int getFieldrefNameAndType(int index)
+ {
FieldrefInfo finfo = (FieldrefInfo)getItem(index);
return finfo.nameAndTypeIndex;
}
@@ -304,17 +386,15 @@ public final class ConstPool {
* @param index an index to a <code>CONSTANT_Fieldref_info</code>.
* @return the name of the field.
*/
- public String getFieldrefName(int index) {
+ public String getFieldrefName(int index)
+ {
FieldrefInfo f = (FieldrefInfo)getItem(index);
if (f == null)
return null;
- else {
- NameAndTypeInfo n = (NameAndTypeInfo)getItem(f.nameAndTypeIndex);
- if(n == null)
- return null;
- else
- return getUtf8Info(n.memberName);
- }
+ NameAndTypeInfo n = (NameAndTypeInfo)getItem(f.nameAndTypeIndex);
+ if(n == null)
+ return null;
+ return getUtf8Info(n.memberName);
}
/**
@@ -325,17 +405,15 @@ public final class ConstPool {
* @param index an index to a <code>CONSTANT_Fieldref_info</code>.
* @return the type descriptor of the field.
*/
- public String getFieldrefType(int index) {
+ public String getFieldrefType(int index)
+ {
FieldrefInfo f = (FieldrefInfo)getItem(index);
if (f == null)
return null;
- else {
- NameAndTypeInfo n = (NameAndTypeInfo)getItem(f.nameAndTypeIndex);
- if(n == null)
- return null;
- else
- return getUtf8Info(n.typeDescriptor);
- }
+ NameAndTypeInfo n = (NameAndTypeInfo)getItem(f.nameAndTypeIndex);
+ if(n == null)
+ return null;
+ return getUtf8Info(n.typeDescriptor);
}
/**
@@ -343,8 +421,9 @@ public final class ConstPool {
* <code>CONSTANT_Methodref_info</code> structure
* at the given index.
*/
- public int getMethodrefClass(int index) {
- MethodrefInfo minfo = (MethodrefInfo)getItem(index);
+ public int getMethodrefClass(int index)
+ {
+ MemberrefInfo minfo = (MemberrefInfo)getItem(index);
return minfo.classIndex;
}
@@ -355,12 +434,12 @@ public final class ConstPool {
*
* @return the name of the class at that <code>class_index</code>.
*/
- public String getMethodrefClassName(int index) {
- MethodrefInfo minfo = (MethodrefInfo)getItem(index);
+ public String getMethodrefClassName(int index)
+ {
+ MemberrefInfo minfo = (MemberrefInfo)getItem(index);
if (minfo == null)
return null;
- else
- return getClassInfo(minfo.classIndex);
+ return getClassInfo(minfo.classIndex);
}
/**
@@ -368,8 +447,9 @@ public final class ConstPool {
* <code>CONSTANT_Methodref_info</code> structure
* at the given index.
*/
- public int getMethodrefNameAndType(int index) {
- MethodrefInfo minfo = (MethodrefInfo)getItem(index);
+ public int getMethodrefNameAndType(int index)
+ {
+ MemberrefInfo minfo = (MemberrefInfo)getItem(index);
return minfo.nameAndTypeIndex;
}
@@ -381,18 +461,16 @@ public final class ConstPool {
* @param index an index to a <code>CONSTANT_Methodref_info</code>.
* @return the name of the method.
*/
- public String getMethodrefName(int index) {
- MethodrefInfo minfo = (MethodrefInfo)getItem(index);
+ public String getMethodrefName(int index)
+ {
+ MemberrefInfo minfo = (MemberrefInfo)getItem(index);
if (minfo == null)
return null;
- else {
- NameAndTypeInfo n
- = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
- if(n == null)
- return null;
- else
- return getUtf8Info(n.memberName);
- }
+ NameAndTypeInfo n
+ = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
+ if(n == null)
+ return null;
+ return getUtf8Info(n.memberName);
}
/**
@@ -403,18 +481,16 @@ public final class ConstPool {
* @param index an index to a <code>CONSTANT_Methodref_info</code>.
* @return the descriptor of the method.
*/
- public String getMethodrefType(int index) {
- MethodrefInfo minfo = (MethodrefInfo)getItem(index);
+ public String getMethodrefType(int index)
+ {
+ MemberrefInfo minfo = (MemberrefInfo)getItem(index);
if (minfo == null)
return null;
- else {
- NameAndTypeInfo n
- = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
- if(n == null)
- return null;
- else
- return getUtf8Info(n.typeDescriptor);
- }
+ NameAndTypeInfo n
+ = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
+ if(n == null)
+ return null;
+ return getUtf8Info(n.typeDescriptor);
}
/**
@@ -422,9 +498,9 @@ public final class ConstPool {
* <code>CONSTANT_InterfaceMethodref_info</code> structure
* at the given index.
*/
- public int getInterfaceMethodrefClass(int index) {
- InterfaceMethodrefInfo minfo
- = (InterfaceMethodrefInfo)getItem(index);
+ public int getInterfaceMethodrefClass(int index)
+ {
+ MemberrefInfo minfo = (MemberrefInfo)getItem(index);
return minfo.classIndex;
}
@@ -435,9 +511,9 @@ public final class ConstPool {
*
* @return the name of the class at that <code>class_index</code>.
*/
- public String getInterfaceMethodrefClassName(int index) {
- InterfaceMethodrefInfo minfo
- = (InterfaceMethodrefInfo)getItem(index);
+ public String getInterfaceMethodrefClassName(int index)
+ {
+ MemberrefInfo minfo = (MemberrefInfo)getItem(index);
return getClassInfo(minfo.classIndex);
}
@@ -446,9 +522,9 @@ public final class ConstPool {
* <code>CONSTANT_InterfaceMethodref_info</code> structure
* at the given index.
*/
- public int getInterfaceMethodrefNameAndType(int index) {
- InterfaceMethodrefInfo minfo
- = (InterfaceMethodrefInfo)getItem(index);
+ public int getInterfaceMethodrefNameAndType(int index)
+ {
+ MemberrefInfo minfo = (MemberrefInfo)getItem(index);
return minfo.nameAndTypeIndex;
}
@@ -461,19 +537,16 @@ public final class ConstPool {
* a <code>CONSTANT_InterfaceMethodref_info</code>.
* @return the name of the method.
*/
- public String getInterfaceMethodrefName(int index) {
- InterfaceMethodrefInfo minfo
- = (InterfaceMethodrefInfo)getItem(index);
+ public String getInterfaceMethodrefName(int index)
+ {
+ MemberrefInfo minfo = (MemberrefInfo)getItem(index);
if (minfo == null)
return null;
- else {
- NameAndTypeInfo n
- = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
- if(n == null)
- return null;
- else
- return getUtf8Info(n.memberName);
- }
+ NameAndTypeInfo n
+ = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
+ if(n == null)
+ return null;
+ return getUtf8Info(n.memberName);
}
/**
@@ -485,19 +558,16 @@ public final class ConstPool {
* a <code>CONSTANT_InterfaceMethodref_info</code>.
* @return the descriptor of the method.
*/
- public String getInterfaceMethodrefType(int index) {
- InterfaceMethodrefInfo minfo
- = (InterfaceMethodrefInfo)getItem(index);
+ public String getInterfaceMethodrefType(int index)
+ {
+ MemberrefInfo minfo = (MemberrefInfo)getItem(index);
if (minfo == null)
return null;
- else {
- NameAndTypeInfo n
- = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
- if(n == null)
- return null;
- else
- return getUtf8Info(n.typeDescriptor);
- }
+ NameAndTypeInfo n
+ = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
+ if(n == null)
+ return null;
+ return getUtf8Info(n.typeDescriptor);
}
/**
* Reads <code>CONSTANT_Integer_info</code>, <code>_Float_info</code>,
@@ -508,21 +578,20 @@ public final class ConstPool {
* @return a <code>String</code> value or a wrapped primitive-type
* value.
*/
- public Object getLdcValue(int index) {
+ public Object getLdcValue(int index)
+ {
ConstInfo constInfo = this.getItem(index);
Object value = null;
if (constInfo instanceof StringInfo)
value = this.getStringInfo(index);
else if (constInfo instanceof FloatInfo)
- value = new Float(getFloatInfo(index));
+ value = Float.valueOf(getFloatInfo(index));
else if (constInfo instanceof IntegerInfo)
- value = new Integer(getIntegerInfo(index));
+ value = Integer.valueOf(getIntegerInfo(index));
else if (constInfo instanceof LongInfo)
- value = new Long(getLongInfo(index));
+ value = Long.valueOf(getLongInfo(index));
else if (constInfo instanceof DoubleInfo)
- value = new Double(getDoubleInfo(index));
- else
- value = null;
+ value = Double.valueOf(getDoubleInfo(index));
return value;
}
@@ -533,7 +602,8 @@ public final class ConstPool {
*
* @return the value specified by this entry.
*/
- public int getIntegerInfo(int index) {
+ public int getIntegerInfo(int index)
+ {
IntegerInfo i = (IntegerInfo)getItem(index);
return i.value;
}
@@ -544,7 +614,8 @@ public final class ConstPool {
*
* @return the value specified by this entry.
*/
- public float getFloatInfo(int index) {
+ public float getFloatInfo(int index)
+ {
FloatInfo i = (FloatInfo)getItem(index);
return i.value;
}
@@ -555,7 +626,8 @@ public final class ConstPool {
*
* @return the value specified by this entry.
*/
- public long getLongInfo(int index) {
+ public long getLongInfo(int index)
+ {
LongInfo i = (LongInfo)getItem(index);
return i.value;
}
@@ -566,7 +638,8 @@ public final class ConstPool {
*
* @return the value specified by this entry.
*/
- public double getDoubleInfo(int index) {
+ public double getDoubleInfo(int index)
+ {
DoubleInfo i = (DoubleInfo)getItem(index);
return i.value;
}
@@ -577,7 +650,8 @@ public final class ConstPool {
*
* @return the string specified by <code>string_index</code>.
*/
- public String getStringInfo(int index) {
+ public String getStringInfo(int index)
+ {
StringInfo si = (StringInfo)getItem(index);
return getUtf8Info(si.string);
}
@@ -588,12 +662,134 @@ public final class ConstPool {
*
* @return the string specified by this entry.
*/
- public String getUtf8Info(int index) {
+ public String getUtf8Info(int index)
+ {
Utf8Info utf = (Utf8Info)getItem(index);
return utf.string;
}
/**
+ * Reads the <code>reference_kind</code> field of the
+ * <code>CONSTANT_MethodHandle_info</code> structure
+ * at the given index.
+ *
+ * @see #REF_getField
+ * @see #REF_getStatic
+ * @see #REF_invokeInterface
+ * @see #REF_invokeSpecial
+ * @see #REF_invokeStatic
+ * @see #REF_invokeVirtual
+ * @see #REF_newInvokeSpecial
+ * @see #REF_putField
+ * @see #REF_putStatic
+ * @since 3.17
+ */
+ public int getMethodHandleKind(int index)
+ {
+ MethodHandleInfo mhinfo = (MethodHandleInfo)getItem(index);
+ return mhinfo.refKind;
+ }
+
+ /**
+ * Reads the <code>reference_index</code> field of the
+ * <code>CONSTANT_MethodHandle_info</code> structure
+ * at the given index.
+ *
+ * @since 3.17
+ */
+ public int getMethodHandleIndex(int index)
+ {
+ MethodHandleInfo mhinfo = (MethodHandleInfo)getItem(index);
+ return mhinfo.refIndex;
+ }
+
+ /**
+ * Reads the <code>descriptor_index</code> field of the
+ * <code>CONSTANT_MethodType_info</code> structure
+ * at the given index.
+ *
+ * @since 3.17
+ */
+ public int getMethodTypeInfo(int index)
+ {
+ MethodTypeInfo mtinfo = (MethodTypeInfo)getItem(index);
+ return mtinfo.descriptor;
+ }
+
+ /**
+ * Reads the <code>bootstrap_method_attr_index</code> field of the
+ * <code>CONSTANT_InvokeDynamic_info</code> structure
+ * at the given index.
+ *
+ * @since 3.17
+ */
+ public int getInvokeDynamicBootstrap(int index)
+ {
+ InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index);
+ return iv.bootstrap;
+ }
+
+ /**
+ * Reads the <code>name_and_type_index</code> field of the
+ * <code>CONSTANT_InvokeDynamic_info</code> structure
+ * at the given index.
+ *
+ * @since 3.17
+ */
+ public int getInvokeDynamicNameAndType(int index)
+ {
+ InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index);
+ return iv.nameAndType;
+ }
+
+ /**
+ * Reads the <code>descriptor_index</code> field of the
+ * <code>CONSTANT_NameAndType_info</code> structure
+ * indirectly specified by the given index.
+ *
+ * @param index an index to a <code>CONSTANT_InvokeDynamic_info</code>.
+ * @return the descriptor of the method.
+ * @since 3.17
+ */
+ public String getInvokeDynamicType(int index)
+ {
+ InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index);
+ if (iv == null)
+ return null;
+ NameAndTypeInfo n = (NameAndTypeInfo)getItem(iv.nameAndType);
+ if(n == null)
+ return null;
+ return getUtf8Info(n.typeDescriptor);
+ }
+
+ /**
+ * Reads the <code>name_index</code> field of the
+ * <code>CONSTANT_Module_info</code> structure at the given index.
+ *
+ * @return the module name at <code>name_index</code>.
+ * @since 3.22
+ */
+ public String getModuleInfo(int index)
+ {
+ ModuleInfo mi = (ModuleInfo)getItem(index);
+ return getUtf8Info(mi.name);
+ }
+
+ /**
+ * Reads the <code>name_index</code> field of the
+ * <code>CONSTANT_Package_info</code> structure at the given index.
+ *
+ * @return the package name at <code>name_index</code>. It is a slash-
+ * separated name such as com/oracle/net.
+ * @since 3.22
+ */
+ public String getPackageInfo(int index)
+ {
+ PackageInfo mi = (PackageInfo)getItem(index);
+ return getUtf8Info(mi.name);
+ }
+
+ /**
* Determines whether <code>CONSTANT_Methodref_info</code>
* structure at the given index represents the constructor
* of the given class.
@@ -603,7 +799,8 @@ public final class ConstPool {
* If it is not that constructor,
* <code>isConstructor()</code> returns 0.
*/
- public int isConstructor(String classname, int index) {
+ public int isConstructor(String classname, int index)
+ {
return isMember(classname, MethodInfo.nameInit, index);
}
@@ -623,7 +820,8 @@ public final class ConstPool {
* If it is not that member,
* <code>isMember()</code> returns 0.
*/
- public int isMember(String classname, String membername, int index) {
+ public int isMember(String classname, String membername, int index)
+ {
MemberrefInfo minfo = (MemberrefInfo)getItem(index);
if (getClassInfo(minfo.classIndex).equals(classname)) {
NameAndTypeInfo ntinfo
@@ -652,22 +850,36 @@ public final class ConstPool {
* Otherwise, null if that structure does not
* match the given member name and descriptor.
*/
- public String eqMember(String membername, String desc, int index) {
+ public String eqMember(String membername, String desc, int index)
+ {
MemberrefInfo minfo = (MemberrefInfo)getItem(index);
NameAndTypeInfo ntinfo
= (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
if (getUtf8Info(ntinfo.memberName).equals(membername)
&& getUtf8Info(ntinfo.typeDescriptor).equals(desc))
return getClassInfo(minfo.classIndex);
- else
- return null; // false
+ return null; // false
}
- private int addItem(ConstInfo info) {
+ private int addItem0(ConstInfo info)
+ {
items.addElement(info);
return numOfItems++;
}
+ private int addItem(ConstInfo info)
+ {
+ if (itemsCache == null)
+ itemsCache = makeItemsCache(items);
+
+ ConstInfo found = itemsCache.get(info);
+ if (found != null)
+ return found.index;
+ items.addElement(info);
+ itemsCache.put(info, info);
+ return numOfItems++;
+ }
+
/**
* Copies the n-th item in this ConstPool object into the destination
* ConstPool object.
@@ -679,7 +891,8 @@ public final class ConstPool {
* @param classnames the map or null.
* @return the index of the copied item into the destination ClassPool.
*/
- public int copy(int n, ConstPool dest, Map classnames) {
+ public int copy(int n, ConstPool dest, Map<String,String> classnames)
+ {
if (n == 0)
return 0;
@@ -688,7 +901,7 @@ public final class ConstPool {
}
int addConstInfoPadding() {
- return addItem(new ConstInfoPadding());
+ return addItem0(new ConstInfoPadding(numOfItems));
}
/**
@@ -699,7 +912,8 @@ public final class ConstPool {
*
* @return the index of the added entry.
*/
- public int addClassInfo(CtClass c) {
+ public int addClassInfo(CtClass c)
+ {
if (c == THIS)
return thisClassInfo;
else if (!c.isArray())
@@ -724,16 +938,10 @@ public final class ConstPool {
* (or the JVM-internal representation of that name).
* @return the index of the added entry.
*/
- public int addClassInfo(String qname) {
- ClassInfo info = (ClassInfo)classes.get(qname);
- if (info != null)
- return info.index;
- else {
- int utf8 = addUtf8Info(Descriptor.toJvmName(qname));
- info = new ClassInfo(utf8, numOfItems);
- classes.put(qname, info);
- return addItem(info);
- }
+ public int addClassInfo(String qname)
+ {
+ int utf8 = addUtf8Info(Descriptor.toJvmName(qname));
+ return addItem(new ClassInfo(utf8, numOfItems));
}
/**
@@ -745,7 +953,8 @@ public final class ConstPool {
* @param type <code>descriptor_index</code>
* @return the index of the added entry.
*/
- public int addNameAndTypeInfo(String name, String type) {
+ public int addNameAndTypeInfo(String name, String type)
+ {
return addNameAndTypeInfo(addUtf8Info(name), addUtf8Info(type));
}
@@ -756,18 +965,9 @@ public final class ConstPool {
* @param type <code>descriptor_index</code>
* @return the index of the added entry.
*/
- public int addNameAndTypeInfo(int name, int type) {
- int h = hashFunc(name, type);
- ConstInfo ci = constInfoCache[h];
- if (ci != null && ci instanceof NameAndTypeInfo && ci.hashCheck(name, type))
- return constInfoIndexCache[h];
- else {
- NameAndTypeInfo item = new NameAndTypeInfo(name, type);
- constInfoCache[h] = item;
- int i = addItem(item);
- constInfoIndexCache[h] = i;
- return i;
- }
+ public int addNameAndTypeInfo(int name, int type)
+ {
+ return addItem(new NameAndTypeInfo(name, type, numOfItems));
}
/**
@@ -783,7 +983,8 @@ public final class ConstPool {
* of <code>CONSTANT_NameAndType_info</code>.
* @return the index of the added entry.
*/
- public int addFieldrefInfo(int classInfo, String name, String type) {
+ public int addFieldrefInfo(int classInfo, String name, String type)
+ {
int nt = addNameAndTypeInfo(name, type);
return addFieldrefInfo(classInfo, nt);
}
@@ -795,18 +996,10 @@ public final class ConstPool {
* @param nameAndTypeInfo <code>name_and_type_index</code>.
* @return the index of the added entry.
*/
- public int addFieldrefInfo(int classInfo, int nameAndTypeInfo) {
- int h = hashFunc(classInfo, nameAndTypeInfo);
- ConstInfo ci = constInfoCache[h];
- if (ci != null && ci instanceof FieldrefInfo && ci.hashCheck(classInfo, nameAndTypeInfo))
- return constInfoIndexCache[h];
- else {
- FieldrefInfo item = new FieldrefInfo(classInfo, nameAndTypeInfo);
- constInfoCache[h] = item;
- int i = addItem(item);
- constInfoIndexCache[h] = i;
- return i;
- }
+ public int addFieldrefInfo(int classInfo, int nameAndTypeInfo)
+ {
+ return addItem(new FieldrefInfo(classInfo, nameAndTypeInfo,
+ numOfItems));
}
/**
@@ -822,7 +1015,8 @@ public final class ConstPool {
* of <code>CONSTANT_NameAndType_info</code>.
* @return the index of the added entry.
*/
- public int addMethodrefInfo(int classInfo, String name, String type) {
+ public int addMethodrefInfo(int classInfo, String name, String type)
+ {
int nt = addNameAndTypeInfo(name, type);
return addMethodrefInfo(classInfo, nt);
}
@@ -834,18 +1028,10 @@ public final class ConstPool {
* @param nameAndTypeInfo <code>name_and_type_index</code>.
* @return the index of the added entry.
*/
- public int addMethodrefInfo(int classInfo, int nameAndTypeInfo) {
- int h = hashFunc(classInfo, nameAndTypeInfo);
- ConstInfo ci = constInfoCache[h];
- if (ci != null && ci instanceof MethodrefInfo && ci.hashCheck(classInfo, nameAndTypeInfo))
- return constInfoIndexCache[h];
- else {
- MethodrefInfo item = new MethodrefInfo(classInfo, nameAndTypeInfo);
- constInfoCache[h] = item;
- int i = addItem(item);
- constInfoIndexCache[h] = i;
- return i;
- }
+ public int addMethodrefInfo(int classInfo, int nameAndTypeInfo)
+ {
+ return addItem(new MethodrefInfo(classInfo,
+ nameAndTypeInfo, numOfItems));
}
/**
@@ -862,8 +1048,10 @@ public final class ConstPool {
* of <code>CONSTANT_NameAndType_info</code>.
* @return the index of the added entry.
*/
- public int addInterfaceMethodrefInfo(int classInfo, String name,
- String type) {
+ public int addInterfaceMethodrefInfo(int classInfo,
+ String name,
+ String type)
+ {
int nt = addNameAndTypeInfo(name, type);
return addInterfaceMethodrefInfo(classInfo, nt);
}
@@ -877,18 +1065,11 @@ public final class ConstPool {
* @return the index of the added entry.
*/
public int addInterfaceMethodrefInfo(int classInfo,
- int nameAndTypeInfo) {
- int h = hashFunc(classInfo, nameAndTypeInfo);
- ConstInfo ci = constInfoCache[h];
- if (ci != null && ci instanceof InterfaceMethodrefInfo && ci.hashCheck(classInfo, nameAndTypeInfo))
- return constInfoIndexCache[h];
- else {
- InterfaceMethodrefInfo item =new InterfaceMethodrefInfo(classInfo, nameAndTypeInfo);
- constInfoCache[h] = item;
- int i = addItem(item);
- constInfoIndexCache[h] = i;
- return i;
- }
+ int nameAndTypeInfo)
+ {
+ return addItem(new InterfaceMethodrefInfo(classInfo,
+ nameAndTypeInfo,
+ numOfItems));
}
/**
@@ -900,8 +1081,10 @@ public final class ConstPool {
*
* @return the index of the added entry.
*/
- public int addStringInfo(String str) {
- return addItem(new StringInfo(addUtf8Info(str)));
+ public int addStringInfo(String str)
+ {
+ int utf = addUtf8Info(str);
+ return addItem(new StringInfo(utf, numOfItems));
}
/**
@@ -910,8 +1093,9 @@ public final class ConstPool {
*
* @return the index of the added entry.
*/
- public int addIntegerInfo(int i) {
- return addItem(new IntegerInfo(i));
+ public int addIntegerInfo(int i)
+ {
+ return addItem(new IntegerInfo(i, numOfItems));
}
/**
@@ -920,8 +1104,9 @@ public final class ConstPool {
*
* @return the index of the added entry.
*/
- public int addFloatInfo(float f) {
- return addItem(new FloatInfo(f));
+ public int addFloatInfo(float f)
+ {
+ return addItem(new FloatInfo(f, numOfItems));
}
/**
@@ -930,9 +1115,12 @@ public final class ConstPool {
*
* @return the index of the added entry.
*/
- public int addLongInfo(long l) {
- int i = addItem(new LongInfo(l));
- addItem(new ConstInfoPadding());
+ public int addLongInfo(long l)
+ {
+ int i = addItem(new LongInfo(l, numOfItems));
+ if (i == numOfItems - 1) // if not existing
+ addConstInfoPadding();
+
return i;
}
@@ -942,9 +1130,12 @@ public final class ConstPool {
*
* @return the index of the added entry.
*/
- public int addDoubleInfo(double d) {
- int i = addItem(new DoubleInfo(d));
- addItem(new ConstInfoPadding());
+ public int addDoubleInfo(double d)
+ {
+ int i = addItem(new DoubleInfo(d, numOfItems));
+ if (i == numOfItems - 1) // if not existing
+ addConstInfoPadding();
+
return i;
}
@@ -952,32 +1143,88 @@ public final class ConstPool {
* Adds a new <code>CONSTANT_Utf8_info</code>
* structure.
*
- * <p>If the given utf8 string has been already recorded in the
- * table, then this method does not add a new entry to avoid adding
- * a duplicated entry.
- * Instead, it returns the index of the entry already recorded.
+ * @return the index of the added entry.
+ */
+ public int addUtf8Info(String utf8)
+ {
+ return addItem(new Utf8Info(utf8, numOfItems));
+ }
+
+ /**
+ * Adds a new <code>CONSTANT_MethodHandle_info</code>
+ * structure.
*
+ * @param kind <code>reference_kind</code>
+ * such as {@link #REF_invokeStatic <code>REF_invokeStatic</code>}.
+ * @param index <code>reference_index</code>.
* @return the index of the added entry.
+ *
+ * @since 3.17
*/
- public int addUtf8Info(String utf8) {
- Utf8Info info = (Utf8Info)strings.get(utf8);
- if (info != null)
- return info.index;
- else {
- info = new Utf8Info(utf8, numOfItems);
- strings.put(utf8, info);
- return addItem(info);
- }
+ public int addMethodHandleInfo(int kind, int index)
+ {
+ return addItem(new MethodHandleInfo(kind, index, numOfItems));
+ }
+
+ /**
+ * Adds a new <code>CONSTANT_MethodType_info</code>
+ * structure.
+ *
+ * @param desc <code>descriptor_index</code>.
+ * @return the index of the added entry.
+ *
+ * @since 3.17
+ */
+ public int addMethodTypeInfo(int desc)
+ {
+ return addItem(new MethodTypeInfo(desc, numOfItems));
+ }
+
+ /**
+ * Adds a new <code>CONSTANT_InvokeDynamic_info</code>
+ * structure.
+ *
+ * @param bootstrap <code>bootstrap_method_attr_index</code>.
+ * @param nameAndType <code>name_and_type_index</code>.
+ * @return the index of the added entry.
+ *
+ * @since 3.17
+ */
+ public int addInvokeDynamicInfo(int bootstrap, int nameAndType)
+ {
+ return addItem(new InvokeDynamicInfo(bootstrap, nameAndType, numOfItems));
+ }
+
+ /**
+ * Adds a new <code>CONSTANT_Module_info</code>
+ * @param nameIndex the index of the Utf8 entry.
+ * @return the index of the added entry.
+ * @since 3.22
+ */
+ public int addModuleInfo(int nameIndex)
+ {
+ return addItem(new ModuleInfo(nameIndex, numOfItems));
+ }
+
+ /**
+ * Adds a new <code>CONSTANT_Package_info</code>
+ * @param nameIndex the index of the Utf8 entry.
+ * @return the index of the added entry.
+ * @since 3.22
+ */
+ public int addPackageInfo(int nameIndex)
+ {
+ return addItem(new PackageInfo(nameIndex, numOfItems));
}
/**
* Get all the class names.
*
- * @return a set of class names
+ * @return a set of class names (<code>String</code> objects).
*/
- public Set getClassNames()
+ public Set<String> getClassNames()
{
- HashSet result = new HashSet();
+ Set<String> result = new HashSet<String>();
LongVector v = items;
int size = numOfItems;
for (int i = 1; i < size; ++i) {
@@ -994,14 +1241,13 @@ public final class ConstPool {
* @param oldName the replaced name (JVM-internal representation).
* @param newName the substituted name (JVM-internal representation).
*/
- public void renameClass(String oldName, String newName) {
+ public void renameClass(String oldName, String newName)
+ {
LongVector v = items;
int size = numOfItems;
- classes = new HashMap(classes.size() * 2);
for (int i = 1; i < size; ++i) {
ConstInfo ci = v.elementAt(i);
- ci.renameClass(this, oldName, newName);
- ci.makeHashtable(this);
+ ci.renameClass(this, oldName, newName, itemsCache);
}
}
@@ -1011,93 +1257,114 @@ public final class ConstPool {
* @param classnames specifies pairs of replaced and substituted
* name.
*/
- public void renameClass(Map classnames) {
+ public void renameClass(Map<String,String> classnames)
+ {
LongVector v = items;
int size = numOfItems;
- classes = new HashMap(classes.size() * 2);
for (int i = 1; i < size; ++i) {
ConstInfo ci = v.elementAt(i);
- ci.renameClass(this, classnames);
- ci.makeHashtable(this);
+ ci.renameClass(this, classnames, itemsCache);
}
}
- private void read(DataInputStream in) throws IOException {
+ private void read(DataInputStream in) throws IOException
+ {
int n = in.readUnsignedShort();
items = new LongVector(n);
numOfItems = 0;
- addItem(null); // index 0 is reserved by the JVM.
+ addItem0(null); // index 0 is reserved by the JVM.
while (--n > 0) { // index 0 is reserved by JVM
int tag = readOne(in);
if ((tag == LongInfo.tag) || (tag == DoubleInfo.tag)) {
- addItem(new ConstInfoPadding());
+ addConstInfoPadding();
--n;
}
}
+ }
+ private static Map<ConstInfo,ConstInfo> makeItemsCache(LongVector items)
+ {
+ Map<ConstInfo,ConstInfo> cache = new HashMap<ConstInfo,ConstInfo>();
int i = 1;
while (true) {
ConstInfo info = items.elementAt(i++);
if (info == null)
break;
- else
- info.makeHashtable(this);
+ cache.put(info, info);
}
+
+ return cache;
}
- private int readOne(DataInputStream in) throws IOException {
+ private int readOne(DataInputStream in) throws IOException
+ {
ConstInfo info;
int tag = in.readUnsignedByte();
switch (tag) {
case Utf8Info.tag : // 1
info = new Utf8Info(in, numOfItems);
- strings.put(((Utf8Info)info).string, info);
break;
case IntegerInfo.tag : // 3
- info = new IntegerInfo(in);
+ info = new IntegerInfo(in, numOfItems);
break;
case FloatInfo.tag : // 4
- info = new FloatInfo(in);
+ info = new FloatInfo(in, numOfItems);
break;
case LongInfo.tag : // 5
- info = new LongInfo(in);
+ info = new LongInfo(in, numOfItems);
break;
case DoubleInfo.tag : // 6
- info = new DoubleInfo(in);
+ info = new DoubleInfo(in, numOfItems);
break;
case ClassInfo.tag : // 7
info = new ClassInfo(in, numOfItems);
- // classes.put(<classname>, info);
break;
case StringInfo.tag : // 8
- info = new StringInfo(in);
+ info = new StringInfo(in, numOfItems);
break;
case FieldrefInfo.tag : // 9
- info = new FieldrefInfo(in);
+ info = new FieldrefInfo(in, numOfItems);
break;
case MethodrefInfo.tag : // 10
- info = new MethodrefInfo(in);
+ info = new MethodrefInfo(in, numOfItems);
break;
case InterfaceMethodrefInfo.tag : // 11
- info = new InterfaceMethodrefInfo(in);
+ info = new InterfaceMethodrefInfo(in, numOfItems);
break;
case NameAndTypeInfo.tag : // 12
- info = new NameAndTypeInfo(in);
+ info = new NameAndTypeInfo(in, numOfItems);
+ break;
+ case MethodHandleInfo.tag : // 15
+ info = new MethodHandleInfo(in, numOfItems);
+ break;
+ case MethodTypeInfo.tag : // 16
+ info = new MethodTypeInfo(in, numOfItems);
+ break;
+ case InvokeDynamicInfo.tag : // 18
+ info = new InvokeDynamicInfo(in, numOfItems);
+ break;
+ case ModuleInfo.tag : // 19
+ info = new ModuleInfo(in, numOfItems);
+ break;
+ case PackageInfo.tag : // 20
+ info = new PackageInfo(in, numOfItems);
break;
default :
- throw new IOException("invalid constant type: " + tag);
+ throw new IOException("invalid constant type: "
+ + tag + " at " + numOfItems);
}
- addItem(info);
+ addItem0(info);
return tag;
}
/**
* Writes the contents of the constant pool table.
*/
- public void write(DataOutputStream out) throws IOException {
+ public void write(DataOutputStream out) throws IOException
+ {
out.writeShort(numOfItems);
LongVector v = items;
int size = numOfItems;
@@ -1108,14 +1375,16 @@ public final class ConstPool {
/**
* Prints the contents of the constant pool table.
*/
- public void print() {
+ public void print()
+ {
print(new PrintWriter(System.out, true));
}
/**
* Prints the contents of the constant pool table.
*/
- public void print(PrintWriter out) {
+ public void print(PrintWriter out)
+ {
int size = numOfItems;
for (int i = 1; i < size; ++i) {
out.print(i);
@@ -1125,22 +1394,27 @@ public final class ConstPool {
}
}
-abstract class ConstInfo {
+abstract class ConstInfo
+{
+ int index;
+
+ public ConstInfo(int i) { index = i; }
+
public abstract int getTag();
public String getClassName(ConstPool cp) { return null; }
- public void renameClass(ConstPool cp, String oldName, String newName) {}
- public void renameClass(ConstPool cp, Map classnames) {}
- public abstract int copy(ConstPool src, ConstPool dest, Map classnames);
- // ** classnames is a mapping between JVM names.
+ public void renameClass(ConstPool cp, String oldName, String newName,
+ Map<ConstInfo,ConstInfo> cache) {}
+ public void renameClass(ConstPool cp, Map<String,String> classnames,
+ Map<ConstInfo,ConstInfo> cache) {}
+ public abstract int copy(ConstPool src, ConstPool dest,
+ Map<String, String> classnames);
+ // ** classnames is a mapping between JVM names.
public abstract void write(DataOutputStream out) throws IOException;
public abstract void print(PrintWriter out);
- void makeHashtable(ConstPool cp) {} // called after read() finishes in ConstPool.
-
- boolean hashCheck(int a, int b) { return false; }
-
+ @Override
public String toString() {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
PrintWriter out = new PrintWriter(bout);
@@ -1151,70 +1425,122 @@ abstract class ConstInfo {
/* padding following DoubleInfo or LongInfo.
*/
-class ConstInfoPadding extends ConstInfo {
+class ConstInfoPadding extends ConstInfo
+{
+ public ConstInfoPadding(int i) { super(i); }
+
+ @Override
public int getTag() { return 0; }
- public int copy(ConstPool src, ConstPool dest, Map map) {
+ @Override
+ public int copy(ConstPool src, ConstPool dest, Map<String,String> map)
+ {
return dest.addConstInfoPadding();
}
+ @Override
public void write(DataOutputStream out) throws IOException {}
- public void print(PrintWriter out) {
+ @Override
+ public void print(PrintWriter out)
+ {
out.println("padding");
}
}
-class ClassInfo extends ConstInfo {
+class ClassInfo extends ConstInfo
+{
static final int tag = 7;
int name;
- int index;
- public ClassInfo(int className, int i) {
+ public ClassInfo(int className, int index)
+ {
+ super(index);
name = className;
- index = i;
}
- public ClassInfo(DataInputStream in, int i) throws IOException {
+ public ClassInfo(DataInputStream in, int index) throws IOException
+ {
+ super(index);
name = in.readUnsignedShort();
- index = i;
}
+ @Override
+ public int hashCode() { return name; }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof ClassInfo && ((ClassInfo)obj).name == name;
+ }
+
+ @Override
public int getTag() { return tag; }
- public String getClassName(ConstPool cp) {
+ @Override
+ public String getClassName(ConstPool cp)
+ {
return cp.getUtf8Info(name);
- };
+ }
- public void renameClass(ConstPool cp, String oldName, String newName) {
+ @Override
+ public void renameClass(ConstPool cp, String oldName, String newName,
+ Map<ConstInfo,ConstInfo> cache)
+ {
String nameStr = cp.getUtf8Info(name);
+ String newNameStr = null;
if (nameStr.equals(oldName))
- name = cp.addUtf8Info(newName);
+ newNameStr = newName;
else if (nameStr.charAt(0) == '[') {
- String nameStr2 = Descriptor.rename(nameStr, oldName, newName);
- if (nameStr != nameStr2)
- name = cp.addUtf8Info(nameStr2);
+ String s = Descriptor.rename(nameStr, oldName, newName);
+ if (nameStr != s)
+ newNameStr = s;
}
+
+ if (newNameStr != null)
+ if (cache == null)
+ name = cp.addUtf8Info(newNameStr);
+ else {
+ cache.remove(this);
+ name = cp.addUtf8Info(newNameStr);
+ cache.put(this, this);
+ }
}
- public void renameClass(ConstPool cp, Map map) {
+ @Override
+ public void renameClass(ConstPool cp, Map<String,String> map,
+ Map<ConstInfo,ConstInfo> cache)
+ {
String oldName = cp.getUtf8Info(name);
+ String newName = null;
if (oldName.charAt(0) == '[') {
- String newName = Descriptor.rename(oldName, map);
- if (oldName != newName)
- name = cp.addUtf8Info(newName);
+ String s = Descriptor.rename(oldName, map);
+ if (oldName != s)
+ newName = s;
}
else {
- String newName = (String)map.get(oldName);
- if (newName != null && !newName.equals(oldName))
+ String s = map.get(oldName);
+ if (s != null && !s.equals(oldName))
+ newName = s;
+ }
+
+ if (newName != null) {
+ if (cache == null)
name = cp.addUtf8Info(newName);
+ else {
+ cache.remove(this);
+ name = cp.addUtf8Info(newName);
+ cache.put(this, this);
+ }
}
}
- public int copy(ConstPool src, ConstPool dest, Map map) {
+ @Override
+ public int copy(ConstPool src, ConstPool dest, Map<String,String> map)
+ {
String classname = src.getUtf8Info(name);
if (map != null) {
- String newname = (String)map.get(classname);
+ String newname = map.get(classname);
if (newname != null)
classname = newname;
}
@@ -1222,56 +1548,93 @@ class ClassInfo extends ConstInfo {
return dest.addClassInfo(classname);
}
- public void write(DataOutputStream out) throws IOException {
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
out.writeByte(tag);
out.writeShort(name);
}
- public void print(PrintWriter out) {
+ @Override
+ public void print(PrintWriter out)
+ {
out.print("Class #");
out.println(name);
}
-
- void makeHashtable(ConstPool cp) {
- String name = Descriptor.toJavaName(getClassName(cp));
- cp.classes.put(name, this);
- }
}
-class NameAndTypeInfo extends ConstInfo {
+class NameAndTypeInfo extends ConstInfo
+{
static final int tag = 12;
int memberName;
int typeDescriptor;
- public NameAndTypeInfo(int name, int type) {
+ public NameAndTypeInfo(int name, int type, int index)
+ {
+ super(index);
memberName = name;
typeDescriptor = type;
}
- public NameAndTypeInfo(DataInputStream in) throws IOException {
+ public NameAndTypeInfo(DataInputStream in, int index) throws IOException
+ {
+ super(index);
memberName = in.readUnsignedShort();
typeDescriptor = in.readUnsignedShort();
}
- boolean hashCheck(int a, int b) { return a == memberName && b == typeDescriptor; }
+ @Override
+ public int hashCode() { return (memberName << 16) ^ typeDescriptor; }
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof NameAndTypeInfo) {
+ NameAndTypeInfo nti = (NameAndTypeInfo)obj;
+ return nti.memberName == memberName
+ && nti.typeDescriptor == typeDescriptor;
+ }
+ return false;
+ }
+
+ @Override
public int getTag() { return tag; }
- public void renameClass(ConstPool cp, String oldName, String newName) {
+ @Override
+ public void renameClass(ConstPool cp, String oldName, String newName,
+ Map<ConstInfo,ConstInfo> cache)
+ {
String type = cp.getUtf8Info(typeDescriptor);
String type2 = Descriptor.rename(type, oldName, newName);
if (type != type2)
- typeDescriptor = cp.addUtf8Info(type2);
+ if (cache == null)
+ typeDescriptor = cp.addUtf8Info(type2);
+ else {
+ cache.remove(this);
+ typeDescriptor = cp.addUtf8Info(type2);
+ cache.put(this, this);
+ }
}
- public void renameClass(ConstPool cp, Map map) {
+ @Override
+ public void renameClass(ConstPool cp, Map<String,String> map,
+ Map<ConstInfo,ConstInfo> cache)
+ {
String type = cp.getUtf8Info(typeDescriptor);
String type2 = Descriptor.rename(type, map);
if (type != type2)
- typeDescriptor = cp.addUtf8Info(type2);
+ if (cache == null)
+ typeDescriptor = cp.addUtf8Info(type2);
+ else {
+ cache.remove(this);
+ typeDescriptor = cp.addUtf8Info(type2);
+ cache.put(this, this);
+ }
}
- public int copy(ConstPool src, ConstPool dest, Map map) {
+ @Override
+ public int copy(ConstPool src, ConstPool dest, Map<String,String> map)
+ {
String mname = src.getUtf8Info(memberName);
String tdesc = src.getUtf8Info(typeDescriptor);
tdesc = Descriptor.rename(tdesc, map);
@@ -1279,12 +1642,14 @@ class NameAndTypeInfo extends ConstInfo {
dest.addUtf8Info(tdesc));
}
+ @Override
public void write(DataOutputStream out) throws IOException {
out.writeByte(tag);
out.writeShort(memberName);
out.writeShort(typeDescriptor);
}
+ @Override
public void print(PrintWriter out) {
out.print("NameAndType #");
out.print(memberName);
@@ -1293,37 +1658,61 @@ class NameAndTypeInfo extends ConstInfo {
}
}
-abstract class MemberrefInfo extends ConstInfo {
+abstract class MemberrefInfo extends ConstInfo
+{
int classIndex;
int nameAndTypeIndex;
- public MemberrefInfo(int cindex, int ntindex) {
+ public MemberrefInfo(int cindex, int ntindex, int thisIndex)
+ {
+ super(thisIndex);
classIndex = cindex;
nameAndTypeIndex = ntindex;
}
- public MemberrefInfo(DataInputStream in) throws IOException {
+ public MemberrefInfo(DataInputStream in, int thisIndex)
+ throws IOException
+ {
+ super(thisIndex);
classIndex = in.readUnsignedShort();
nameAndTypeIndex = in.readUnsignedShort();
}
- public int copy(ConstPool src, ConstPool dest, Map map) {
+ @Override
+ public int hashCode() { return (classIndex << 16) ^ nameAndTypeIndex; }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof MemberrefInfo) {
+ MemberrefInfo mri = (MemberrefInfo)obj;
+ return mri.classIndex == classIndex
+ && mri.nameAndTypeIndex == nameAndTypeIndex
+ && mri.getClass() == this.getClass();
+ }
+ return false;
+ }
+
+ @Override
+ public int copy(ConstPool src, ConstPool dest, Map<String,String> map)
+ {
int classIndex2 = src.getItem(classIndex).copy(src, dest, map);
int ntIndex2 = src.getItem(nameAndTypeIndex).copy(src, dest, map);
return copy2(dest, classIndex2, ntIndex2);
}
- boolean hashCheck(int a, int b) { return a == classIndex && b == nameAndTypeIndex; }
-
abstract protected int copy2(ConstPool dest, int cindex, int ntindex);
- public void write(DataOutputStream out) throws IOException {
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
out.writeByte(getTag());
out.writeShort(classIndex);
out.writeShort(nameAndTypeIndex);
}
- public void print(PrintWriter out) {
+ @Override
+ public void print(PrintWriter out)
+ {
out.print(getTagName() + " #");
out.print(classIndex);
out.print(", name&type #");
@@ -1333,240 +1722,720 @@ abstract class MemberrefInfo extends ConstInfo {
public abstract String getTagName();
}
-class FieldrefInfo extends MemberrefInfo {
+class FieldrefInfo extends MemberrefInfo
+{
static final int tag = 9;
- public FieldrefInfo(int cindex, int ntindex) {
- super(cindex, ntindex);
+ public FieldrefInfo(int cindex, int ntindex, int thisIndex)
+ {
+ super(cindex, ntindex, thisIndex);
}
- public FieldrefInfo(DataInputStream in) throws IOException {
- super(in);
+ public FieldrefInfo(DataInputStream in, int thisIndex)
+ throws IOException
+ {
+ super(in, thisIndex);
}
+ @Override
public int getTag() { return tag; }
+ @Override
public String getTagName() { return "Field"; }
- protected int copy2(ConstPool dest, int cindex, int ntindex) {
+ @Override
+ protected int copy2(ConstPool dest, int cindex, int ntindex)
+ {
return dest.addFieldrefInfo(cindex, ntindex);
}
}
-class MethodrefInfo extends MemberrefInfo {
+class MethodrefInfo extends MemberrefInfo
+{
static final int tag = 10;
- public MethodrefInfo(int cindex, int ntindex) {
- super(cindex, ntindex);
+ public MethodrefInfo(int cindex, int ntindex, int thisIndex)
+ {
+ super(cindex, ntindex, thisIndex);
}
- public MethodrefInfo(DataInputStream in) throws IOException {
- super(in);
+ public MethodrefInfo(DataInputStream in, int thisIndex)
+ throws IOException
+ {
+ super(in, thisIndex);
}
+ @Override
public int getTag() { return tag; }
+ @Override
public String getTagName() { return "Method"; }
- protected int copy2(ConstPool dest, int cindex, int ntindex) {
+ @Override
+ protected int copy2(ConstPool dest, int cindex, int ntindex)
+ {
return dest.addMethodrefInfo(cindex, ntindex);
}
}
-class InterfaceMethodrefInfo extends MemberrefInfo {
+class InterfaceMethodrefInfo extends MemberrefInfo
+{
static final int tag = 11;
- public InterfaceMethodrefInfo(int cindex, int ntindex) {
- super(cindex, ntindex);
+ public InterfaceMethodrefInfo(int cindex, int ntindex, int thisIndex)
+ {
+ super(cindex, ntindex, thisIndex);
}
- public InterfaceMethodrefInfo(DataInputStream in) throws IOException {
- super(in);
+ public InterfaceMethodrefInfo(DataInputStream in, int thisIndex)
+ throws IOException
+ {
+ super(in, thisIndex);
}
+ @Override
public int getTag() { return tag; }
+ @Override
public String getTagName() { return "Interface"; }
- protected int copy2(ConstPool dest, int cindex, int ntindex) {
+ @Override
+ protected int copy2(ConstPool dest, int cindex, int ntindex)
+ {
return dest.addInterfaceMethodrefInfo(cindex, ntindex);
}
}
-class StringInfo extends ConstInfo {
+class StringInfo extends ConstInfo
+{
static final int tag = 8;
int string;
- public StringInfo(int str) {
+ public StringInfo(int str, int index)
+ {
+ super(index);
string = str;
}
- public StringInfo(DataInputStream in) throws IOException {
+ public StringInfo(DataInputStream in, int index) throws IOException
+ {
+ super(index);
string = in.readUnsignedShort();
}
+ @Override
+ public int hashCode() { return string; }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof StringInfo && ((StringInfo)obj).string == string;
+ }
+
+ @Override
public int getTag() { return tag; }
- public int copy(ConstPool src, ConstPool dest, Map map) {
+ @Override
+ public int copy(ConstPool src, ConstPool dest, Map<String,String> map)
+ {
return dest.addStringInfo(src.getUtf8Info(string));
}
- public void write(DataOutputStream out) throws IOException {
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
out.writeByte(tag);
out.writeShort(string);
}
- public void print(PrintWriter out) {
+ @Override
+ public void print(PrintWriter out)
+ {
out.print("String #");
out.println(string);
}
}
-class IntegerInfo extends ConstInfo {
+class IntegerInfo extends ConstInfo
+{
static final int tag = 3;
int value;
- public IntegerInfo(int i) {
- value = i;
+ public IntegerInfo(int v, int index)
+ {
+ super(index);
+ value = v;
}
- public IntegerInfo(DataInputStream in) throws IOException {
+ public IntegerInfo(DataInputStream in, int index) throws IOException
+ {
+ super(index);
value = in.readInt();
}
+ @Override
+ public int hashCode() { return value; }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof IntegerInfo && ((IntegerInfo)obj).value == value;
+ }
+
+ @Override
public int getTag() { return tag; }
- public int copy(ConstPool src, ConstPool dest, Map map) {
+ @Override
+ public int copy(ConstPool src, ConstPool dest, Map<String,String> map)
+ {
return dest.addIntegerInfo(value);
}
- public void write(DataOutputStream out) throws IOException {
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
out.writeByte(tag);
out.writeInt(value);
}
- public void print(PrintWriter out) {
+ @Override
+ public void print(PrintWriter out)
+ {
out.print("Integer ");
out.println(value);
}
}
-class FloatInfo extends ConstInfo {
+class FloatInfo extends ConstInfo
+{
static final int tag = 4;
float value;
- public FloatInfo(float f) {
+ public FloatInfo(float f, int index)
+ {
+ super(index);
value = f;
}
- public FloatInfo(DataInputStream in) throws IOException {
+ public FloatInfo(DataInputStream in, int index) throws IOException
+ {
+ super(index);
value = in.readFloat();
}
+ @Override
+ public int hashCode() { return Float.floatToIntBits(value); }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof FloatInfo && ((FloatInfo)obj).value == value;
+ }
+
+ @Override
public int getTag() { return tag; }
- public int copy(ConstPool src, ConstPool dest, Map map) {
+ @Override
+ public int copy(ConstPool src, ConstPool dest, Map<String,String> map)
+ {
return dest.addFloatInfo(value);
}
- public void write(DataOutputStream out) throws IOException {
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
out.writeByte(tag);
out.writeFloat(value);
}
- public void print(PrintWriter out) {
+ @Override
+ public void print(PrintWriter out)
+ {
out.print("Float ");
out.println(value);
}
}
-class LongInfo extends ConstInfo {
+class LongInfo extends ConstInfo
+{
static final int tag = 5;
long value;
- public LongInfo(long l) {
+ public LongInfo(long l, int index)
+ {
+ super(index);
value = l;
}
- public LongInfo(DataInputStream in) throws IOException {
+ public LongInfo(DataInputStream in, int index) throws IOException
+ {
+ super(index);
value = in.readLong();
}
+ @Override
+ public int hashCode() { return (int)(value ^ (value >>> 32)); }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LongInfo && ((LongInfo)obj).value == value;
+ }
+
+ @Override
public int getTag() { return tag; }
- public int copy(ConstPool src, ConstPool dest, Map map) {
+ @Override
+ public int copy(ConstPool src, ConstPool dest, Map<String,String> map)
+ {
return dest.addLongInfo(value);
}
- public void write(DataOutputStream out) throws IOException {
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
out.writeByte(tag);
out.writeLong(value);
}
- public void print(PrintWriter out) {
+ @Override
+ public void print(PrintWriter out)
+ {
out.print("Long ");
out.println(value);
}
}
-class DoubleInfo extends ConstInfo {
+class DoubleInfo extends ConstInfo
+{
static final int tag = 6;
double value;
- public DoubleInfo(double d) {
+ public DoubleInfo(double d, int index)
+ {
+ super(index);
value = d;
}
- public DoubleInfo(DataInputStream in) throws IOException {
+ public DoubleInfo(DataInputStream in, int index) throws IOException
+ {
+ super(index);
value = in.readDouble();
}
+ @Override
+ public int hashCode() {
+ long v = Double.doubleToLongBits(value);
+ return (int)(v ^ (v >>> 32));
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof DoubleInfo
+ && ((DoubleInfo)obj).value == value;
+ }
+
+ @Override
public int getTag() { return tag; }
- public int copy(ConstPool src, ConstPool dest, Map map) {
+ @Override
+ public int copy(ConstPool src, ConstPool dest, Map<String,String> map)
+ {
return dest.addDoubleInfo(value);
}
- public void write(DataOutputStream out) throws IOException {
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
out.writeByte(tag);
out.writeDouble(value);
}
- public void print(PrintWriter out) {
+ @Override
+ public void print(PrintWriter out)
+ {
out.print("Double ");
out.println(value);
}
}
-class Utf8Info extends ConstInfo {
+class Utf8Info extends ConstInfo
+{
static final int tag = 1;
String string;
- int index;
- public Utf8Info(String utf8, int i) {
+ public Utf8Info(String utf8, int index)
+ {
+ super(index);
string = utf8;
- index = i;
}
- public Utf8Info(DataInputStream in, int i) throws IOException {
+ public Utf8Info(DataInputStream in, int index)
+ throws IOException
+ {
+ super(index);
string = in.readUTF();
- index = i;
}
+ @Override
+ public int hashCode() {
+ return string.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Utf8Info
+ && ((Utf8Info)obj).string.equals(string);
+ }
+
+ @Override
public int getTag() { return tag; }
- public int copy(ConstPool src, ConstPool dest, Map map) {
+ @Override
+ public int copy(ConstPool src, ConstPool dest,
+ Map<String,String> map)
+ {
return dest.addUtf8Info(string);
}
- public void write(DataOutputStream out) throws IOException {
+ @Override
+ public void write(DataOutputStream out)
+ throws IOException
+ {
out.writeByte(tag);
out.writeUTF(string);
}
+ @Override
public void print(PrintWriter out) {
out.print("UTF8 \"");
out.print(string);
out.println("\"");
}
}
+
+class MethodHandleInfo extends ConstInfo {
+ static final int tag = 15;
+ int refKind, refIndex;
+
+ public MethodHandleInfo(int kind, int referenceIndex, int index) {
+ super(index);
+ refKind = kind;
+ refIndex = referenceIndex;
+ }
+
+ public MethodHandleInfo(DataInputStream in, int index)
+ throws IOException
+ {
+ super(index);
+ refKind = in.readUnsignedByte();
+ refIndex = in.readUnsignedShort();
+ }
+
+ @Override
+ public int hashCode() { return (refKind << 16) ^ refIndex; }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof MethodHandleInfo) {
+ MethodHandleInfo mh = (MethodHandleInfo)obj;
+ return mh.refKind == refKind && mh.refIndex == refIndex;
+ }
+ return false;
+ }
+
+ @Override
+ public int getTag() { return tag; }
+
+ @Override
+ public int copy(ConstPool src, ConstPool dest,
+ Map<String,String> map)
+ {
+ return dest.addMethodHandleInfo(refKind,
+ src.getItem(refIndex).copy(src, dest, map));
+ }
+
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
+ out.writeByte(tag);
+ out.writeByte(refKind);
+ out.writeShort(refIndex);
+ }
+
+ @Override
+ public void print(PrintWriter out) {
+ out.print("MethodHandle #");
+ out.print(refKind);
+ out.print(", index #");
+ out.println(refIndex);
+ }
+}
+
+class MethodTypeInfo extends ConstInfo
+{
+ static final int tag = 16;
+ int descriptor;
+
+ public MethodTypeInfo(int desc, int index)
+ {
+ super(index);
+ descriptor = desc;
+ }
+
+ public MethodTypeInfo(DataInputStream in, int index)
+ throws IOException
+ {
+ super(index);
+ descriptor = in.readUnsignedShort();
+ }
+
+ @Override
+ public int hashCode() { return descriptor; }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof MethodTypeInfo)
+ return ((MethodTypeInfo)obj).descriptor == descriptor;
+ return false;
+ }
+
+ @Override
+ public int getTag() { return tag; }
+
+ @Override
+ public void renameClass(ConstPool cp, String oldName, String newName,
+ Map<ConstInfo,ConstInfo> cache)
+ {
+ String desc = cp.getUtf8Info(descriptor);
+ String desc2 = Descriptor.rename(desc, oldName, newName);
+ if (desc != desc2)
+ if (cache == null)
+ descriptor = cp.addUtf8Info(desc2);
+ else {
+ cache.remove(this);
+ descriptor = cp.addUtf8Info(desc2);
+ cache.put(this, this);
+ }
+ }
+
+ @Override
+ public void renameClass(ConstPool cp, Map<String,String> map,
+ Map<ConstInfo,ConstInfo> cache)
+ {
+ String desc = cp.getUtf8Info(descriptor);
+ String desc2 = Descriptor.rename(desc, map);
+ if (desc != desc2)
+ if (cache == null)
+ descriptor = cp.addUtf8Info(desc2);
+ else {
+ cache.remove(this);
+ descriptor = cp.addUtf8Info(desc2);
+ cache.put(this, this);
+ }
+ }
+
+ @Override
+ public int copy(ConstPool src, ConstPool dest, Map<String,String> map)
+ {
+ String desc = src.getUtf8Info(descriptor);
+ desc = Descriptor.rename(desc, map);
+ return dest.addMethodTypeInfo(dest.addUtf8Info(desc));
+ }
+
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
+ out.writeByte(tag);
+ out.writeShort(descriptor);
+ }
+
+ @Override
+ public void print(PrintWriter out) {
+ out.print("MethodType #");
+ out.println(descriptor);
+ }
+}
+
+class InvokeDynamicInfo extends ConstInfo
+{
+ static final int tag = 18;
+ int bootstrap, nameAndType;
+
+ public InvokeDynamicInfo(int bootstrapMethod,
+ int ntIndex, int index)
+ {
+ super(index);
+ bootstrap = bootstrapMethod;
+ nameAndType = ntIndex;
+ }
+
+ public InvokeDynamicInfo(DataInputStream in, int index)
+ throws IOException
+ {
+ super(index);
+ bootstrap = in.readUnsignedShort();
+ nameAndType = in.readUnsignedShort();
+ }
+
+ @Override
+ public int hashCode() { return (bootstrap << 16) ^ nameAndType; }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof InvokeDynamicInfo) {
+ InvokeDynamicInfo iv = (InvokeDynamicInfo)obj;
+ return iv.bootstrap == bootstrap
+ && iv.nameAndType == nameAndType;
+ }
+ return false;
+ }
+
+ @Override
+ public int getTag() { return tag; }
+
+ @Override
+ public int copy(ConstPool src, ConstPool dest,
+ Map<String,String> map)
+ {
+ return dest.addInvokeDynamicInfo(bootstrap,
+ src.getItem(nameAndType).copy(src, dest, map));
+ }
+
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
+ out.writeByte(tag);
+ out.writeShort(bootstrap);
+ out.writeShort(nameAndType);
+ }
+
+ @Override
+ public void print(PrintWriter out) {
+ out.print("InvokeDynamic #");
+ out.print(bootstrap);
+ out.print(", name&type #");
+ out.println(nameAndType);
+ }
+}
+
+class ModuleInfo extends ConstInfo
+{
+ static final int tag = 19;
+ int name;
+
+ public ModuleInfo(int moduleName, int index)
+ {
+ super(index);
+ name = moduleName;
+ }
+
+ public ModuleInfo(DataInputStream in, int index)
+ throws IOException
+ {
+ super(index);
+ name = in.readUnsignedShort();
+ }
+
+ @Override
+ public int hashCode() { return name; }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof ModuleInfo
+ && ((ModuleInfo)obj).name == name;
+ }
+
+ @Override
+ public int getTag() { return tag; }
+
+ public String getModuleName(ConstPool cp)
+ {
+ return cp.getUtf8Info(name);
+ }
+
+ @Override
+ public int copy(ConstPool src, ConstPool dest,
+ Map<String,String> map)
+ {
+ String moduleName = src.getUtf8Info(name);
+ int newName = dest.addUtf8Info(moduleName);
+ return dest.addModuleInfo(newName);
+ }
+
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
+ out.writeByte(tag);
+ out.writeShort(name);
+ }
+
+ @Override
+ public void print(PrintWriter out) {
+ out.print("Module #");
+ out.println(name);
+ }
+}
+
+class PackageInfo extends ConstInfo
+{
+ static final int tag = 20;
+ int name;
+
+ public PackageInfo(int moduleName, int index)
+ {
+ super(index);
+ name = moduleName;
+ }
+
+ public PackageInfo(DataInputStream in, int index)
+ throws IOException
+ {
+ super(index);
+ name = in.readUnsignedShort();
+ }
+
+ @Override
+ public int hashCode() { return name; }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof PackageInfo
+ && ((PackageInfo)obj).name == name;
+ }
+
+ @Override
+ public int getTag() { return tag; }
+
+ public String getPackageName(ConstPool cp)
+ {
+ return cp.getUtf8Info(name);
+ }
+
+ @Override
+ public int copy(ConstPool src, ConstPool dest,
+ Map<String,String> map)
+ {
+ String packageName = src.getUtf8Info(name);
+ int newName = dest.addUtf8Info(packageName);
+ return dest.addModuleInfo(newName);
+ }
+
+ @Override
+ public void write(DataOutputStream out) throws IOException
+ {
+ out.writeByte(tag);
+ out.writeShort(name);
+ }
+
+ @Override
+ public void print(PrintWriter out)
+ {
+ out.print("Package #");
+ out.println(name);
+ }
+}
diff --git a/src/main/javassist/bytecode/ConstantAttribute.java b/src/main/javassist/bytecode/ConstantAttribute.java
index 47f993b..40c395c 100644
--- a/src/main/javassist/bytecode/ConstantAttribute.java
+++ b/src/main/javassist/bytecode/ConstantAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,8 +17,8 @@
package javassist.bytecode;
import java.io.DataInputStream;
-import java.util.Map;
import java.io.IOException;
+import java.util.Map;
/**
* <code>ConstantValue_attribute</code>.
@@ -64,7 +65,8 @@ public class ConstantAttribute extends AttributeInfo {
* @param classnames pairs of replaced and substituted
* class names.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
int index = getConstPool().copy(getConstantValue(), newCp,
classnames);
return new ConstantAttribute(newCp, index);
diff --git a/src/main/javassist/bytecode/DeprecatedAttribute.java b/src/main/javassist/bytecode/DeprecatedAttribute.java
index 41099ce..eaf39ef 100644
--- a/src/main/javassist/bytecode/DeprecatedAttribute.java
+++ b/src/main/javassist/bytecode/DeprecatedAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -49,7 +50,8 @@ public class DeprecatedAttribute extends AttributeInfo {
* @param newCp the constant pool table used by the new copy.
* @param classnames should be null.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
return new DeprecatedAttribute(newCp);
}
}
diff --git a/src/main/javassist/bytecode/Descriptor.java b/src/main/javassist/bytecode/Descriptor.java
index 5662222..fc49b87 100644
--- a/src/main/javassist/bytecode/Descriptor.java
+++ b/src/main/javassist/bytecode/Descriptor.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,12 @@
package javassist.bytecode;
+import java.util.Map;
+
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtPrimitiveType;
import javassist.NotFoundException;
-import java.util.Map;
/**
* A support class for dealing with descriptors.
@@ -58,8 +60,7 @@ public class Descriptor {
public static String toJvmName(CtClass clazz) {
if (clazz.isArray())
return of(clazz);
- else
- return toJvmName(clazz.getName());
+ return toJvmName(clazz.getName());
}
/**
@@ -108,14 +109,12 @@ public class Descriptor {
if (arrayDim == 0)
return name;
- else {
- StringBuffer sbuf = new StringBuffer(name);
- do {
- sbuf.append("[]");
- } while (--arrayDim > 0);
+ StringBuffer sbuf = new StringBuffer(name);
+ do {
+ sbuf.append("[]");
+ } while (--arrayDim > 0);
- return sbuf.toString();
- }
+ return sbuf.toString();
}
/**
@@ -182,13 +181,11 @@ public class Descriptor {
if (head == 0)
return desc;
- else {
- int len = desc.length();
- if (head < len)
- newdesc.append(desc.substring(head, len));
+ int len = desc.length();
+ if (head < len)
+ newdesc.append(desc.substring(head, len));
- return newdesc.toString();
- }
+ return newdesc.toString();
}
/**
@@ -199,7 +196,7 @@ public class Descriptor {
* JVM class names.
* @see Descriptor#toJvmName(String)
*/
- public static String rename(String desc, Map map) {
+ public static String rename(String desc, Map<String,String> map) {
if (map == null)
return desc;
@@ -217,7 +214,7 @@ public class Descriptor {
i = k + 1;
String name = desc.substring(j + 1, k);
- String name2 = (String)map.get(name);
+ String name2 = map.get(name);
if (name2 != null) {
newdesc.append(desc.substring(head, j));
newdesc.append('L');
@@ -229,13 +226,11 @@ public class Descriptor {
if (head == 0)
return desc;
- else {
- int len = desc.length();
- if (head < len)
- newdesc.append(desc.substring(head, len));
+ int len = desc.length();
+ if (head < len)
+ newdesc.append(desc.substring(head, len));
- return newdesc.toString();
- }
+ return newdesc.toString();
}
/**
@@ -328,15 +323,13 @@ public class Descriptor {
int i = desc.indexOf(')');
if (i < 0)
return desc;
- else {
- StringBuffer newdesc = new StringBuffer();
- newdesc.append(desc.substring(0, i));
- newdesc.append('L');
- newdesc.append(classname.replace('.', '/'));
- newdesc.append(';');
- newdesc.append(desc.substring(i));
- return newdesc.toString();
- }
+ StringBuffer newdesc = new StringBuffer();
+ newdesc.append(desc.substring(0, i));
+ newdesc.append('L');
+ newdesc.append(classname.replace('.', '/'));
+ newdesc.append(';');
+ newdesc.append(desc.substring(i));
+ return newdesc.toString();
}
/**
@@ -352,9 +345,8 @@ public class Descriptor {
public static String insertParameter(String classname, String desc) {
if (desc.charAt(0) != '(')
return desc;
- else
- return "(L" + classname.replace('.', '/') + ';'
- + desc.substring(1);
+ return "(L" + classname.replace('.', '/') + ';'
+ + desc.substring(1);
}
/**
@@ -369,13 +361,11 @@ public class Descriptor {
int i = descriptor.indexOf(')');
if (i < 0)
return descriptor;
- else {
- StringBuffer newdesc = new StringBuffer();
- newdesc.append(descriptor.substring(0, i));
- toDescriptor(newdesc, type);
- newdesc.append(descriptor.substring(i));
- return newdesc.toString();
- }
+ StringBuffer newdesc = new StringBuffer();
+ newdesc.append(descriptor.substring(0, i));
+ toDescriptor(newdesc, type);
+ newdesc.append(descriptor.substring(i));
+ return newdesc.toString();
}
/**
@@ -390,8 +380,7 @@ public class Descriptor {
String descriptor) {
if (descriptor.charAt(0) != '(')
return descriptor;
- else
- return "(" + of(type) + descriptor.substring(1);
+ return "(" + of(type) + descriptor.substring(1);
}
/**
@@ -406,14 +395,12 @@ public class Descriptor {
int i = desc.indexOf(')');
if (i < 0)
return desc;
- else {
- StringBuffer newdesc = new StringBuffer();
- newdesc.append(desc.substring(0, i + 1));
- newdesc.append('L');
- newdesc.append(classname.replace('.', '/'));
- newdesc.append(';');
- return newdesc.toString();
- }
+ StringBuffer newdesc = new StringBuffer();
+ newdesc.append(desc.substring(0, i + 1));
+ newdesc.append('L');
+ newdesc.append(classname.replace('.', '/'));
+ newdesc.append(';');
+ return newdesc.toString();
}
/**
@@ -429,16 +416,14 @@ public class Descriptor {
{
if (desc.charAt(0) != '(')
return null;
- else {
- int num = numOfParameters(desc);
- CtClass[] args = new CtClass[num];
- int n = 0;
- int i = 1;
- do {
- i = toCtClass(cp, desc, i, args, n++);
- } while (i > 0);
- return args;
- }
+ int num = numOfParameters(desc);
+ CtClass[] args = new CtClass[num];
+ int n = 0;
+ int i = 1;
+ do {
+ i = toCtClass(cp, desc, i, args, n++);
+ } while (i > 0);
+ return args;
}
/**
@@ -483,11 +468,9 @@ public class Descriptor {
int i = desc.indexOf(')');
if (i < 0)
return null;
- else {
- CtClass[] type = new CtClass[1];
- toCtClass(cp, desc, i + 1, type, 0);
- return type[0];
- }
+ CtClass[] type = new CtClass[1];
+ toCtClass(cp, desc, i + 1, type, 0);
+ return type[0];
}
/**
@@ -541,11 +524,9 @@ public class Descriptor {
int res = toCtClass(cp, desc, 0, clazz, 0);
if (res >= 0)
return clazz[0];
- else {
- // maybe, you forgot to surround the class name with
- // L and ;. It violates the protocol, but I'm tolerant...
- return cp.get(desc.replace('/', '.'));
- }
+ // maybe, you forgot to surround the class name with
+ // L and ;. It violates the protocol, but I'm tolerant...
+ return cp.get(desc.replace('/', '.'));
}
private static int toCtClass(ClassPool cp, String desc, int i,
@@ -576,8 +557,7 @@ public class Descriptor {
args[n] = type;
return i2; // neither an array type or a class type
}
- else
- name = type.getName();
+ name = type.getName();
}
if (arrayDim > 0) {
diff --git a/src/main/javassist/bytecode/DuplicateMemberException.java b/src/main/javassist/bytecode/DuplicateMemberException.java
index 7c1e0e6..5a2edf5 100644
--- a/src/main/javassist/bytecode/DuplicateMemberException.java
+++ b/src/main/javassist/bytecode/DuplicateMemberException.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -24,6 +25,9 @@ import javassist.CannotCompileException;
* @see ClassFile#addField(FieldInfo)
*/
public class DuplicateMemberException extends CannotCompileException {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public DuplicateMemberException(String msg) {
super(msg);
}
diff --git a/src/main/javassist/bytecode/EnclosingMethodAttribute.java b/src/main/javassist/bytecode/EnclosingMethodAttribute.java
index c924f50..9eee2a0 100644
--- a/src/main/javassist/bytecode/EnclosingMethodAttribute.java
+++ b/src/main/javassist/bytecode/EnclosingMethodAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -97,10 +98,14 @@ public class EnclosingMethodAttribute extends AttributeInfo {
/**
* Returns the method name specified by <code>method_index</code>.
+ * If the method is a class initializer (static constructor),
+ * {@link MethodInfo#nameClinit} is returned.
*/
public String methodName() {
ConstPool cp = getConstPool();
int mi = methodIndex();
+ if (mi == 0)
+ return MethodInfo.nameClinit;
int ni = cp.getNameAndTypeName(mi);
return cp.getUtf8Info(ni);
}
@@ -123,11 +128,11 @@ public class EnclosingMethodAttribute extends AttributeInfo {
* @param classnames pairs of replaced and substituted
* class names.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
- if (methodIndex() == 0)
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
+ if (methodIndex() == 0)
return new EnclosingMethodAttribute(newCp, className());
- else
- return new EnclosingMethodAttribute(newCp, className(),
- methodName(), methodDescriptor());
+ return new EnclosingMethodAttribute(newCp, className(),
+ methodName(), methodDescriptor());
}
}
diff --git a/src/main/javassist/bytecode/ExceptionTable.java b/src/main/javassist/bytecode/ExceptionTable.java
index 1c74e6e..d4e5e25 100644
--- a/src/main/javassist/bytecode/ExceptionTable.java
+++ b/src/main/javassist/bytecode/ExceptionTable.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -19,6 +20,7 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
class ExceptionTableEntry {
@@ -40,7 +42,7 @@ class ExceptionTableEntry {
*/
public class ExceptionTable implements Cloneable {
private ConstPool constPool;
- private ArrayList entries;
+ private List<ExceptionTableEntry> entries;
/**
* Constructs an <code>exception_table[]</code>.
@@ -49,13 +51,13 @@ public class ExceptionTable implements Cloneable {
*/
public ExceptionTable(ConstPool cp) {
constPool = cp;
- entries = new ArrayList();
+ entries = new ArrayList<ExceptionTableEntry>();
}
ExceptionTable(ConstPool cp, DataInputStream in) throws IOException {
constPool = cp;
int length = in.readUnsignedShort();
- ArrayList list = new ArrayList(length);
+ List<ExceptionTableEntry> list = new ArrayList<ExceptionTableEntry>(length);
for (int i = 0; i < length; ++i) {
int start = in.readUnsignedShort();
int end = in.readUnsignedShort();
@@ -72,9 +74,10 @@ public class ExceptionTable implements Cloneable {
* The constant pool object is shared between this object
* and the cloned object.
*/
+ @Override
public Object clone() throws CloneNotSupportedException {
ExceptionTable r = (ExceptionTable)super.clone();
- r.entries = new ArrayList(entries);
+ r.entries = new ArrayList<ExceptionTableEntry>(entries);
return r;
}
@@ -92,8 +95,7 @@ public class ExceptionTable implements Cloneable {
* @param nth the <i>n</i>-th (&gt;= 0).
*/
public int startPc(int nth) {
- ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
- return e.startPc;
+ return entries.get(nth).startPc;
}
/**
@@ -103,8 +105,7 @@ public class ExceptionTable implements Cloneable {
* @param value new value.
*/
public void setStartPc(int nth, int value) {
- ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
- e.startPc = value;
+ entries.get(nth).startPc = value;
}
/**
@@ -113,8 +114,7 @@ public class ExceptionTable implements Cloneable {
* @param nth the <i>n</i>-th (&gt;= 0).
*/
public int endPc(int nth) {
- ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
- return e.endPc;
+ return entries.get(nth).endPc;
}
/**
@@ -124,8 +124,7 @@ public class ExceptionTable implements Cloneable {
* @param value new value.
*/
public void setEndPc(int nth, int value) {
- ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
- e.endPc = value;
+ entries.get(nth).endPc = value;
}
/**
@@ -134,8 +133,7 @@ public class ExceptionTable implements Cloneable {
* @param nth the <i>n</i>-th (&gt;= 0).
*/
public int handlerPc(int nth) {
- ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
- return e.handlerPc;
+ return entries.get(nth).handlerPc;
}
/**
@@ -145,8 +143,7 @@ public class ExceptionTable implements Cloneable {
* @param value new value.
*/
public void setHandlerPc(int nth, int value) {
- ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
- e.handlerPc = value;
+ entries.get(nth).handlerPc = value;
}
/**
@@ -157,8 +154,7 @@ public class ExceptionTable implements Cloneable {
* or zero if this exception handler is for all exceptions.
*/
public int catchType(int nth) {
- ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
- return e.catchType;
+ return entries.get(nth).catchType;
}
/**
@@ -168,8 +164,7 @@ public class ExceptionTable implements Cloneable {
* @param value new value.
*/
public void setCatchType(int nth, int value) {
- ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
- e.catchType = value;
+ entries.get(nth).catchType = value;
}
/**
@@ -182,8 +177,7 @@ public class ExceptionTable implements Cloneable {
public void add(int index, ExceptionTable table, int offset) {
int len = table.size();
while (--len >= 0) {
- ExceptionTableEntry e
- = (ExceptionTableEntry)table.entries.get(len);
+ ExceptionTableEntry e = table.entries.get(len);
add(index, e.startPc + offset, e.endPc + offset,
e.handlerPc + offset, e.catchType);
}
@@ -235,12 +229,10 @@ public class ExceptionTable implements Cloneable {
* @param classnames pairs of replaced and substituted
* class names.
*/
- public ExceptionTable copy(ConstPool newCp, Map classnames) {
+ public ExceptionTable copy(ConstPool newCp, Map<String,String> classnames) {
ExceptionTable et = new ExceptionTable(newCp);
ConstPool srcCp = constPool;
- int len = size();
- for (int i = 0; i < len; ++i) {
- ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i);
+ for (ExceptionTableEntry e:entries) {
int type = srcCp.copy(e.catchType, newCp, classnames);
et.add(e.startPc, e.endPc, e.handlerPc, type);
}
@@ -249,9 +241,7 @@ public class ExceptionTable implements Cloneable {
}
void shiftPc(int where, int gapLength, boolean exclusive) {
- int len = size();
- for (int i = 0; i < len; ++i) {
- ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i);
+ for (ExceptionTableEntry e:entries) {
e.startPc = shiftPc(e.startPc, where, gapLength, exclusive);
e.endPc = shiftPc(e.endPc, where, gapLength, exclusive);
e.handlerPc = shiftPc(e.handlerPc, where, gapLength, exclusive);
@@ -267,10 +257,8 @@ public class ExceptionTable implements Cloneable {
}
void write(DataOutputStream out) throws IOException {
- int len = size();
- out.writeShort(len); // exception_table_length
- for (int i = 0; i < len; ++i) {
- ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i);
+ out.writeShort(size()); // exception_table_length
+ for (ExceptionTableEntry e:entries) {
out.writeShort(e.startPc);
out.writeShort(e.endPc);
out.writeShort(e.handlerPc);
diff --git a/src/main/javassist/bytecode/ExceptionsAttribute.java b/src/main/javassist/bytecode/ExceptionsAttribute.java
index 2fe34dd..670e51e 100644
--- a/src/main/javassist/bytecode/ExceptionsAttribute.java
+++ b/src/main/javassist/bytecode/ExceptionsAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -41,7 +42,7 @@ public class ExceptionsAttribute extends AttributeInfo {
* @param src source attribute.
*/
private ExceptionsAttribute(ConstPool cp, ExceptionsAttribute src,
- Map classnames) {
+ Map<String,String> classnames) {
super(cp, tag);
copyFrom(src, classnames);
}
@@ -66,7 +67,8 @@ public class ExceptionsAttribute extends AttributeInfo {
* @param classnames pairs of replaced and substituted
* class names. It can be <code>null</code>.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
return new ExceptionsAttribute(newCp, this, classnames);
}
@@ -78,7 +80,7 @@ public class ExceptionsAttribute extends AttributeInfo {
* @param classnames pairs of replaced and substituted
* class names.
*/
- private void copyFrom(ExceptionsAttribute srcAttr, Map classnames) {
+ private void copyFrom(ExceptionsAttribute srcAttr, Map<String,String> classnames) {
ConstPool srcCp = srcAttr.constPool;
ConstPool destCp = this.constPool;
byte[] src = srcAttr.info;
diff --git a/src/main/javassist/bytecode/FieldInfo.java b/src/main/javassist/bytecode/FieldInfo.java
index f474373..7d26327 100644
--- a/src/main/javassist/bytecode/FieldInfo.java
+++ b/src/main/javassist/bytecode/FieldInfo.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -18,12 +19,21 @@ package javassist.bytecode;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
-import java.util.List;
import java.util.ArrayList;
+import java.util.List;
/**
* <code>field_info</code> structure.
*
+ * <p>The following code adds a public field <code>width</code>
+ * of <code>int</code> type:
+ * <blockquote><pre>
+ * ClassFile cf = ...
+ * FieldInfo f = new FieldInfo(cf.getConstPool(), "width", "I");
+ * f.setAccessFlags(AccessFlag.PUBLIC);
+ * cf.addField(f);
+ * </pre></blockquote>
+ *
* @see javassist.CtField#getFieldInfo()
*/
public final class FieldInfo {
@@ -33,7 +43,7 @@ public final class FieldInfo {
String cachedName;
String cachedType;
int descriptor;
- ArrayList attribute; // may be null.
+ List<AttributeInfo> attribute; // may be null.
private FieldInfo(ConstPool cp) {
constPool = cp;
@@ -65,6 +75,7 @@ public final class FieldInfo {
/**
* Returns a string representation of the object.
*/
+ @Override
public String toString() {
return getName() + " " + getDescriptor();
}
@@ -85,7 +96,7 @@ public final class FieldInfo {
}
void prune(ConstPool cp) {
- ArrayList newAttributes = new ArrayList();
+ List<AttributeInfo> newAttributes = new ArrayList<AttributeInfo>();
AttributeInfo invisibleAnnotations
= getAttribute(AnnotationsAttribute.invisibleTag);
if (invisibleAnnotations != null) {
@@ -100,13 +111,13 @@ public final class FieldInfo {
newAttributes.add(visibleAnnotations);
}
- AttributeInfo signature
+ AttributeInfo signature
= getAttribute(SignatureAttribute.tag);
if (signature != null) {
signature = signature.copy(cp, null);
newAttributes.add(signature);
}
-
+
int index = getConstantValue();
if (index != 0) {
index = constPool.copy(index, cp, null);
@@ -196,8 +207,7 @@ public final class FieldInfo {
= (ConstantAttribute)getAttribute(ConstantAttribute.tag);
if (attr == null)
return 0;
- else
- return attr.getConstantValue();
+ return attr.getConstantValue();
}
/**
@@ -210,9 +220,9 @@ public final class FieldInfo {
* @return a list of <code>AttributeInfo</code> objects.
* @see AttributeInfo
*/
- public List getAttributes() {
+ public List<AttributeInfo> getAttributes() {
if (attribute == null)
- attribute = new ArrayList();
+ attribute = new ArrayList<AttributeInfo>();
return attribute;
}
@@ -221,6 +231,11 @@ public final class FieldInfo {
* Returns the attribute with the specified name.
* It returns null if the specified attribute is not found.
*
+ * <p>An attribute name can be obtained by, for example,
+ * {@link AnnotationsAttribute#visibleTag} or
+ * {@link AnnotationsAttribute#invisibleTag}.
+ * </p>
+ *
* @param name attribute name
* @see #getAttributes()
*/
@@ -229,6 +244,17 @@ public final class FieldInfo {
}
/**
+ * Removes an attribute with the specified name.
+ *
+ * @param name attribute name.
+ * @return the removed attribute or null.
+ * @since 3.21
+ */
+ public AttributeInfo removeAttribute(String name) {
+ return AttributeInfo.remove(attribute, name);
+ }
+
+ /**
* Appends an attribute. If there is already an attribute with
* the same name, the new one substitutes for it.
*
@@ -236,7 +262,7 @@ public final class FieldInfo {
*/
public void addAttribute(AttributeInfo info) {
if (attribute == null)
- attribute = new ArrayList();
+ attribute = new ArrayList<AttributeInfo>();
AttributeInfo.remove(attribute, info.getName());
attribute.add(info);
@@ -247,7 +273,7 @@ public final class FieldInfo {
name = in.readUnsignedShort();
descriptor = in.readUnsignedShort();
int n = in.readUnsignedShort();
- attribute = new ArrayList();
+ attribute = new ArrayList<AttributeInfo>();
for (int i = 0; i < n; ++i)
attribute.add(AttributeInfo.read(constPool, in));
}
diff --git a/src/main/javassist/bytecode/InnerClassesAttribute.java b/src/main/javassist/bytecode/InnerClassesAttribute.java
index df5645a..1bce893 100644
--- a/src/main/javassist/bytecode/InnerClassesAttribute.java
+++ b/src/main/javassist/bytecode/InnerClassesAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,8 +17,8 @@
package javassist.bytecode;
import java.io.DataInputStream;
-import java.util.Map;
import java.io.IOException;
+import java.util.Map;
/**
* <code>InnerClasses_attribute</code>.
@@ -63,15 +64,16 @@ public class InnerClassesAttribute extends AttributeInfo {
/**
* Returns the class name indicated
* by <code>classes[nth].inner_class_info_index</code>.
+ * The class name is fully-qualified and separated by dot.
*
* @return null or the class name.
+ * @see ConstPool#getClassInfo(int)
*/
public String innerClass(int nth) {
int i = innerClassIndex(nth);
if (i == 0)
return null;
- else
- return constPool.getClassInfo(i);
+ return constPool.getClassInfo(i);
}
/**
@@ -99,8 +101,7 @@ public class InnerClassesAttribute extends AttributeInfo {
int i = outerClassIndex(nth);
if (i == 0)
return null;
- else
- return constPool.getClassInfo(i);
+ return constPool.getClassInfo(i);
}
/**
@@ -128,8 +129,7 @@ public class InnerClassesAttribute extends AttributeInfo {
int i = innerNameIndex(nth);
if (i == 0)
return null;
- else
- return constPool.getUtf8Info(i);
+ return constPool.getUtf8Info(i);
}
/**
@@ -156,6 +156,22 @@ public class InnerClassesAttribute extends AttributeInfo {
}
/**
+ * Finds the entry for the given inner class.
+ *
+ * @param name the fully-qualified class name separated by dot and $.
+ * @return the index or -1 if not found.
+ * @since 3.22
+ */
+ public int find(String name) {
+ int n = tableLength();
+ for (int i = 0; i < n; i++)
+ if (name.equals(innerClass(i)))
+ return i;
+
+ return -1;
+ }
+
+ /**
* Appends a new entry.
*
* @param inner <code>inner_class_info_index</code>
@@ -197,6 +213,40 @@ public class InnerClassesAttribute extends AttributeInfo {
}
/**
+ * Removes the {@code nth} entry. It does not eliminate
+ * constant pool items that the removed entry refers to.
+ * {@link ClassFile#compact()} should be executed to remove
+ * these unnecessary items.
+ *
+ * @param nth 0, 1, 2, ...
+ * @return the number of items after the removal.
+ * @see ClassFile#compact()
+ */
+ public int remove(int nth) {
+ byte[] data = get();
+ int len = data.length;
+ if (len < 10)
+ return 0;
+
+ int n = ByteArray.readU16bit(data, 0);
+ int nthPos = 2 + nth * 8;
+ if (n <= nth)
+ return n;
+
+ byte[] newData = new byte[len - 8];
+ ByteArray.write16bit(n - 1, newData, 0);
+ int i = 2, j = 2;
+ while (i < len)
+ if (i == nthPos)
+ i += 8;
+ else
+ newData[j++] = data[i++];
+
+ set(newData);
+ return n - 1;
+ }
+
+ /**
* Makes a copy. Class names are replaced according to the
* given <code>Map</code> object.
*
@@ -204,7 +254,8 @@ public class InnerClassesAttribute extends AttributeInfo {
* @param classnames pairs of replaced and substituted
* class names.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
byte[] src = get();
byte[] dest = new byte[src.length];
ConstPool cp = getConstPool();
diff --git a/src/main/javassist/bytecode/InstructionPrinter.java b/src/main/javassist/bytecode/InstructionPrinter.java
index f0a20e1..93182ba 100644
--- a/src/main/javassist/bytecode/InstructionPrinter.java
+++ b/src/main/javassist/bytecode/InstructionPrinter.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -19,7 +20,7 @@ import java.io.PrintStream;
import javassist.CtMethod;
/**
- * Simple utility class for printing the instructions of a method.
+ * Simple utility class for printing the bytecode instructions of a method.
*
* @author Jason T. Greene
*/
@@ -28,14 +29,23 @@ public class InstructionPrinter implements Opcode {
private final static String opcodes[] = Mnemonic.OPCODE;
private final PrintStream stream;
+ /**
+ * Constructs a <code>InstructionPrinter</code> object.
+ */
public InstructionPrinter(PrintStream stream) {
this.stream = stream;
}
+ /**
+ * Prints the bytecode instructions of a given method.
+ */
public static void print(CtMethod method, PrintStream stream) {
(new InstructionPrinter(stream)).print(method);
}
+ /**
+ * Prints the bytecode instructions of a given method.
+ */
public void print(CtMethod method) {
MethodInfo info = method.getMethodInfo2();
ConstPool pool = info.getConstPool();
@@ -56,6 +66,10 @@ public class InstructionPrinter implements Opcode {
}
}
+ /**
+ * Gets a string representation of the bytecode instruction at the specified
+ * position.
+ */
public static String instructionString(CodeIterator iter, int pos, ConstPool pool) {
int opcode = iter.byteAt(pos);
@@ -102,7 +116,7 @@ public class InstructionPrinter implements Opcode {
case IF_ICMPNE:
return opstring + " " + (iter.s16bitAt(pos + 1) + pos);
case IINC:
- return opstring + " " + iter.byteAt(pos + 1);
+ return opstring + " " + iter.byteAt(pos + 1) + ", " + iter.signedByteAt(pos + 2);
case GOTO:
case JSR:
return opstring + " " + (iter.s16bitAt(pos + 1) + pos);
@@ -123,8 +137,8 @@ public class InstructionPrinter implements Opcode {
return opstring + " " + methodInfo(pool, iter.u16bitAt(pos + 1));
case INVOKEINTERFACE:
return opstring + " " + interfaceMethodInfo(pool, iter.u16bitAt(pos + 1));
- case 186:
- throw new RuntimeException("Bad opcode 186");
+ case INVOKEDYNAMIC:
+ return opstring + " " + iter.u16bitAt(pos + 1);
case NEW:
return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1));
case NEWARRAY:
diff --git a/src/main/javassist/bytecode/LineNumberAttribute.java b/src/main/javassist/bytecode/LineNumberAttribute.java
index f384d2f..cead96e 100644
--- a/src/main/javassist/bytecode/LineNumberAttribute.java
+++ b/src/main/javassist/bytecode/LineNumberAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -155,7 +156,8 @@ public class LineNumberAttribute extends AttributeInfo {
* @param newCp the constant pool table used by the new copy.
* @param classnames should be null.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
byte[] src = info;
int num = src.length;
byte[] dest = new byte[num];
diff --git a/src/main/javassist/bytecode/LocalVariableAttribute.java b/src/main/javassist/bytecode/LocalVariableAttribute.java
index 3d44a29..a0a62cc 100644
--- a/src/main/javassist/bytecode/LocalVariableAttribute.java
+++ b/src/main/javassist/bytecode/LocalVariableAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -52,6 +53,7 @@ public class LocalVariableAttribute extends AttributeInfo {
* @since 3.1
* @deprecated
*/
+ @Deprecated
public LocalVariableAttribute(ConstPool cp, String name) {
super(cp, name, new byte[2]);
ByteArray.write16bit(0, info, 0);
@@ -92,6 +94,7 @@ public class LocalVariableAttribute extends AttributeInfo {
info = newInfo;
}
+ @Override
void renameClass(String oldname, String newname) {
ConstPool cp = getConstPool();
int n = tableLength();
@@ -110,7 +113,8 @@ public class LocalVariableAttribute extends AttributeInfo {
return Descriptor.rename(desc, oldname, newname);
}
- void renameClass(Map classnames) {
+ @Override
+ void renameClass(Map<String,String> classnames) {
ConstPool cp = getConstPool();
int n = tableLength();
for (int i = 0; i < n; ++i) {
@@ -124,7 +128,7 @@ public class LocalVariableAttribute extends AttributeInfo {
}
}
- String renameEntry(String desc, Map classnames) {
+ String renameEntry(String desc, Map<String,String> classnames) {
return Descriptor.rename(desc, classnames);
}
@@ -290,7 +294,8 @@ public class LocalVariableAttribute extends AttributeInfo {
* @param newCp the constant pool table used by the new copy.
* @param classnames should be null.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
byte[] src = get();
byte[] dest = new byte[src.length];
ConstPool cp = getConstPool();
diff --git a/src/main/javassist/bytecode/LocalVariableTypeAttribute.java b/src/main/javassist/bytecode/LocalVariableTypeAttribute.java
index d7ac098..12a5bf7 100644
--- a/src/main/javassist/bytecode/LocalVariableTypeAttribute.java
+++ b/src/main/javassist/bytecode/LocalVariableTypeAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -48,14 +49,17 @@ public class LocalVariableTypeAttribute extends LocalVariableAttribute {
super(cp, tag, dest);
}
+ @Override
String renameEntry(String desc, String oldname, String newname) {
return SignatureAttribute.renameClass(desc, oldname, newname);
}
- String renameEntry(String desc, Map classnames) {
+ @Override
+ String renameEntry(String desc, Map<String,String> classnames) {
return SignatureAttribute.renameClass(desc, classnames);
}
+ @Override
LocalVariableAttribute makeThisAttr(ConstPool cp, byte[] dest) {
return new LocalVariableTypeAttribute(cp, dest);
}
diff --git a/src/main/javassist/bytecode/LongVector.java b/src/main/javassist/bytecode/LongVector.java
index 1f76b4a..7e1db72 100644
--- a/src/main/javassist/bytecode/LongVector.java
+++ b/src/main/javassist/bytecode/LongVector.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/bytecode/MethodInfo.java b/src/main/javassist/bytecode/MethodInfo.java
index aae98ea..313fde9 100644
--- a/src/main/javassist/bytecode/MethodInfo.java
+++ b/src/main/javassist/bytecode/MethodInfo.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -21,12 +22,34 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+
import javassist.ClassPool;
import javassist.bytecode.stackmap.MapMaker;
/**
* <code>method_info</code> structure.
+ *
+ * <p>The bytecode sequence of the method is represented
+ * by a <code>CodeAttribute</code> object.
+ *
+ * <p>The following code adds the default constructor to a class:
+ * of <code>int</code> type:
+ * <blockquote><pre>
+ * ClassFile cf = ...
+ * Bytecode code = new Bytecode(cf.getConstPool());
+ * code.addAload(0);
+ * code.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V");
+ * code.addReturn(null);
+ * code.setMaxLocals(1);
+ *
+ * MethodInfo minfo = new MethodInfo(cf.getConstPool(), MethodInfo.nameInit, "()V");
+ * minfo.setCodeAttribute(code.toCodeAttribute());
+ * cf.addMethod(minfo);
+ * </pre></blockquote>
*
+ * @see #getCodeAttribute()
+ * @see CodeAttribute
+ * @see Bytecode
* @see javassist.CtMethod#getMethodInfo()
* @see javassist.CtConstructor#getMethodInfo()
*/
@@ -36,7 +59,7 @@ public class MethodInfo {
int name;
String cachedName;
int descriptor;
- ArrayList attribute; // may be null
+ List<AttributeInfo> attribute; // may be null
/**
* If this value is true, Javassist maintains a <code>StackMap</code> attribute
@@ -46,13 +69,13 @@ public class MethodInfo {
public static boolean doPreverify = false;
/**
- * The name of constructors: <code>&lt;init&gt</code>.
+ * The name of constructors: <code>&lt;init&gt;</code>.
*/
public static final String nameInit = "<init>";
/**
* The name of class initializer (static initializer):
- * <code>&lt;clinit&gt</code>.
+ * <code>&lt;clinit&gt;</code>.
*/
public static final String nameClinit = "<clinit>";
@@ -106,7 +129,8 @@ public class MethodInfo {
* @see Descriptor
*/
public MethodInfo(ConstPool cp, String methodname, MethodInfo src,
- Map classnameMap) throws BadBytecode {
+ Map<String,String> classnameMap) throws BadBytecode
+ {
this(cp);
read(src, methodname, classnameMap);
}
@@ -114,6 +138,7 @@ public class MethodInfo {
/**
* Returns a string representation of the object.
*/
+ @Override
public String toString() {
return getName() + " " + getDescriptor();
}
@@ -134,7 +159,7 @@ public class MethodInfo {
}
void prune(ConstPool cp) {
- ArrayList newAttributes = new ArrayList();
+ List<AttributeInfo> newAttributes = new ArrayList<AttributeInfo>();
AttributeInfo invisibleAnnotations
= getAttribute(AnnotationsAttribute.invisibleTag);
@@ -281,9 +306,9 @@ public class MethodInfo {
* @return a list of <code>AttributeInfo</code> objects.
* @see AttributeInfo
*/
- public List getAttributes() {
+ public List<AttributeInfo> getAttributes() {
if (attribute == null)
- attribute = new ArrayList();
+ attribute = new ArrayList<AttributeInfo>();
return attribute;
}
@@ -292,6 +317,11 @@ public class MethodInfo {
* Returns the attribute with the specified name. If it is not found, this
* method returns null.
*
+ * <p>An attribute name can be obtained by, for example,
+ * {@link AnnotationsAttribute#visibleTag} or
+ * {@link AnnotationsAttribute#invisibleTag}.
+ * </p>
+ *
* @param name attribute name
* @return an <code>AttributeInfo</code> object or null.
* @see #getAttributes()
@@ -301,6 +331,17 @@ public class MethodInfo {
}
/**
+ * Removes an attribute with the specified name.
+ *
+ * @param name attribute name.
+ * @return the removed attribute or null.
+ * @since 3.21
+ */
+ public AttributeInfo removeAttribute(String name) {
+ return AttributeInfo.remove(attribute, name);
+ }
+
+ /**
* Appends an attribute. If there is already an attribute with the same
* name, the new one substitutes for it.
*
@@ -308,7 +349,7 @@ public class MethodInfo {
*/
public void addAttribute(AttributeInfo info) {
if (attribute == null)
- attribute = new ArrayList();
+ attribute = new ArrayList<AttributeInfo>();
AttributeInfo.remove(attribute, info.getName());
attribute.add(info);
@@ -352,7 +393,7 @@ public class MethodInfo {
public void setExceptionsAttribute(ExceptionsAttribute cattr) {
removeExceptionsAttribute();
if (attribute == null)
- attribute = new ArrayList();
+ attribute = new ArrayList<AttributeInfo>();
attribute.add(cattr);
}
@@ -374,7 +415,7 @@ public class MethodInfo {
public void setCodeAttribute(CodeAttribute cattr) {
removeCodeAttribute();
if (attribute == null)
- attribute = new ArrayList();
+ attribute = new ArrayList<AttributeInfo>();
attribute.add(cattr);
}
@@ -389,6 +430,7 @@ public class MethodInfo {
* @param cf rebuild if this class file is for Java 6 or later.
* @see #rebuildStackMap(ClassPool)
* @see #rebuildStackMapForME(ClassPool)
+ * @see #doPreverify
* @since 3.6
*/
public void rebuildStackMapIf6(ClassPool pool, ClassFile cf)
@@ -424,7 +466,7 @@ public class MethodInfo {
* include a code attribute, nothing happens.
*
* @param pool used for making type hierarchy.
- * @see StackMapTable
+ * @see StackMap
* @since 3.12
*/
public void rebuildStackMapForME(ClassPool pool) throws BadBytecode {
@@ -496,8 +538,7 @@ public class MethodInfo {
}
}
- private void read(MethodInfo src, String methodname, Map classnames)
- throws BadBytecode {
+ private void read(MethodInfo src, String methodname, Map<String,String> classnames) {
ConstPool destCp = constPool;
accessFlags = src.accessFlags;
name = destCp.addUtf8Info(methodname);
@@ -507,7 +548,7 @@ public class MethodInfo {
String desc2 = Descriptor.rename(desc, classnames);
descriptor = destCp.addUtf8Info(desc2);
- attribute = new ArrayList();
+ attribute = new ArrayList<AttributeInfo>();
ExceptionsAttribute eattr = src.getExceptionsAttribute();
if (eattr != null)
attribute.add(eattr.copy(destCp, classnames));
@@ -522,7 +563,7 @@ public class MethodInfo {
name = in.readUnsignedShort();
descriptor = in.readUnsignedShort();
int n = in.readUnsignedShort();
- attribute = new ArrayList();
+ attribute = new ArrayList<AttributeInfo>();
for (int i = 0; i < n; ++i)
attribute.add(AttributeInfo.read(constPool, in));
}
diff --git a/src/main/javassist/bytecode/MethodParametersAttribute.java b/src/main/javassist/bytecode/MethodParametersAttribute.java
new file mode 100644
index 0000000..1252109
--- /dev/null
+++ b/src/main/javassist/bytecode/MethodParametersAttribute.java
@@ -0,0 +1,88 @@
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <code>MethodParameters_attribute</code>.
+ */
+public class MethodParametersAttribute extends AttributeInfo {
+ /**
+ * The name of this attribute <code>"MethodParameters"</code>.
+ */
+ public static final String tag = "MethodParameters";
+
+ MethodParametersAttribute(ConstPool cp, int n, DataInputStream in)
+ throws IOException
+ {
+ super(cp, n, in);
+ }
+
+ /**
+ * Constructs an attribute.
+ *
+ * @param cp a constant pool table.
+ * @param names an array of parameter names.
+ * The i-th element is the name of the i-th parameter.
+ * @param flags an array of parameter access flags.
+ */
+ public MethodParametersAttribute(ConstPool cp, String[] names, int[] flags) {
+ super(cp, tag);
+ byte[] data = new byte[names.length * 4 + 1];
+ data[0] = (byte)names.length;
+ for (int i = 0; i < names.length; i++) {
+ ByteArray.write16bit(cp.addUtf8Info(names[i]), data, i * 4 + 1);
+ ByteArray.write16bit(flags[i], data, i * 4 + 3);
+ }
+
+ set(data);
+ }
+
+ /**
+ * Returns <code>parameters_count</code>, which is the number of
+ * parameters.
+ */
+ public int size() {
+ return info[0] & 0xff;
+ }
+
+ /**
+ * Returns the value of <code>name_index</code> of the i-th element of <code>parameters</code>.
+ *
+ * @param i the position of the parameter.
+ */
+ public int name(int i) {
+ return ByteArray.readU16bit(info, i * 4 + 1);
+ }
+
+ /**
+ * Returns the value of <code>access_flags</code> of the i-th element of <code>parameters</code>.
+ *
+ * @param i the position of the parameter.
+ * @see AccessFlag
+ */
+ public int accessFlags(int i) {
+ return ByteArray.readU16bit(info, i * 4 + 3);
+ }
+
+ /**
+ * Makes a copy.
+ *
+ * @param newCp the constant pool table used by the new copy.
+ * @param classnames ignored.
+ */
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
+ int s = size();
+ ConstPool cp = getConstPool();
+ String[] names = new String[s];
+ int[] flags = new int[s];
+ for (int i = 0; i < s; i++) {
+ names[i] = cp.getUtf8Info(name(i));
+ flags[i] = accessFlags(i);
+ }
+
+ return new MethodParametersAttribute(newCp, names, flags);
+ }
+}
diff --git a/src/main/javassist/bytecode/Mnemonic.java b/src/main/javassist/bytecode/Mnemonic.java
index 2eb596a..0872655 100644
--- a/src/main/javassist/bytecode/Mnemonic.java
+++ b/src/main/javassist/bytecode/Mnemonic.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -30,9 +31,6 @@ public interface Mnemonic {
/**
* The instruction names (mnemonics) sorted by the opcode.
* The length of this array is 202 (jsr_w=201).
- *
- * <p>The value at index 186 is null since no instruction is
- * assigned to 186.
*/
String[] OPCODE = {
"nop", /* 0*/
@@ -221,7 +219,7 @@ public interface Mnemonic {
"invokespecial", /* 183*/
"invokestatic", /* 184*/
"invokeinterface", /* 185*/
- null,
+ "invokedynamic", /* 186 */
"new", /* 187*/
"newarray", /* 188*/
"anewarray", /* 189*/
diff --git a/src/main/javassist/bytecode/NestHostAttribute.java b/src/main/javassist/bytecode/NestHostAttribute.java
new file mode 100644
index 0000000..28466d5
--- /dev/null
+++ b/src/main/javassist/bytecode/NestHostAttribute.java
@@ -0,0 +1,67 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <code>NestHost_attribute</code>.
+ * It was introduced by JEP-181. See JVMS 4.7.28 for the specification.
+ *
+ * @since 3.24
+ */
+public class NestHostAttribute extends AttributeInfo {
+ /**
+ * The name of this attribute <code>"NestHost"</code>.
+ */
+ public static final String tag = "NestHost";
+
+ NestHostAttribute(ConstPool cp, int n, DataInputStream in) throws IOException {
+ super(cp, n, in);
+ }
+
+ private NestHostAttribute(ConstPool cp, int hostIndex) {
+ super(cp, tag, new byte[2]);
+ ByteArray.write16bit(hostIndex, get(), 0);
+ }
+
+ /**
+ * Makes a copy. Class names are replaced according to the
+ * given <code>Map</code> object.
+ *
+ * @param newCp the constant pool table used by the new copy.
+ * @param classnames pairs of replaced and substituted
+ * class names.
+ */
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String, String> classnames) {
+ int hostIndex = ByteArray.readU16bit(get(), 0);
+ int newHostIndex = getConstPool().copy(hostIndex, newCp, classnames);
+ return new NestHostAttribute(newCp, newHostIndex);
+ }
+
+ /**
+ * Returns <code>host_class_index</code>. The constant pool entry
+ * at this entry is a <code>CONSTANT_Class_info</code> structure.
+ * @return the value of <code>host_class_index</code>.
+ */
+ public int hostClassIndex() {
+ return ByteArray.readU16bit(info, 0);
+ }
+}
diff --git a/src/main/javassist/bytecode/NestMembersAttribute.java b/src/main/javassist/bytecode/NestMembersAttribute.java
new file mode 100644
index 0000000..a402e26
--- /dev/null
+++ b/src/main/javassist/bytecode/NestMembersAttribute.java
@@ -0,0 +1,88 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <code>NestMembers_attribute</code>.
+ * It was introduced by JEP-181. See JVMS 4.7.29 for the specification.
+ *
+ * @since 3.24
+ */
+public class NestMembersAttribute extends AttributeInfo {
+ /**
+ * The name of this attribute <code>"NestMembers"</code>.
+ */
+ public static final String tag = "NestMembers";
+
+ NestMembersAttribute(ConstPool cp, int n, DataInputStream in) throws IOException {
+ super(cp, n, in);
+ }
+
+ private NestMembersAttribute(ConstPool cp, byte[] info) {
+ super(cp, tag, info);
+ }
+
+ /**
+ * Makes a copy. Class names are replaced according to the
+ * given <code>Map</code> object.
+ *
+ * @param newCp the constant pool table used by the new copy.
+ * @param classnames pairs of replaced and substituted
+ * class names.
+ */
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String, String> classnames) {
+ byte[] src = get();
+ byte[] dest = new byte[src.length];
+ ConstPool cp = getConstPool();
+
+ int n = ByteArray.readU16bit(src, 0);
+ ByteArray.write16bit(n, dest, 0);
+
+ for (int i = 0, j = 2; i < n; ++i, j += 2) {
+ int index = ByteArray.readU16bit(src, j);
+ int newIndex = cp.copy(index, newCp, classnames);
+ ByteArray.write16bit(newIndex, dest, j);
+ }
+
+ return new NestMembersAttribute(newCp, dest);
+ }
+
+ /**
+ * Returns <code>number_of_classes</code>.
+ * @return the number of the classes recorded in this attribute.
+ */
+ public int numberOfClasses() {
+ return ByteArray.readU16bit(info, 0);
+ }
+
+ /** Returns <code>classes[index]</code>.
+ *
+ * @param index the index into <code>classes</code>.
+ * @return the value at the given index in the <code>classes</code> array.
+ * It is an index into the constant pool.
+ * The constant pool entry at the returned index is a
+ * <code>CONSTANT_Class_info</code> structure.
+ */
+ public int memberClass(int index) {
+ return ByteArray.readU16bit(info, index * 2 + 2);
+ }
+}
diff --git a/src/main/javassist/bytecode/Opcode.java b/src/main/javassist/bytecode/Opcode.java
index db34b11..54555bd 100644
--- a/src/main/javassist/bytecode/Opcode.java
+++ b/src/main/javassist/bytecode/Opcode.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -155,6 +156,7 @@ public interface Opcode {
int IMUL = 104;
int INEG = 116;
int INSTANCEOF = 193;
+ int INVOKEDYNAMIC = 186;
int INVOKEINTERFACE = 185;
int INVOKESPECIAL = 183;
int INVOKESTATIC = 184;
@@ -427,7 +429,7 @@ public interface Opcode {
0, // invokespecial, 183 depends on the type
0, // invokestatic, 184 depends on the type
0, // invokeinterface, 185 depends on the type
- 0, // undefined, 186
+ 0, // invokedynaimc, 186 depends on the type
1, // new, 187
0, // newarray, 188
0, // anewarray, 189
diff --git a/src/main/javassist/bytecode/ParameterAnnotationsAttribute.java b/src/main/javassist/bytecode/ParameterAnnotationsAttribute.java
index 246afc1..49e2646 100644
--- a/src/main/javassist/bytecode/ParameterAnnotationsAttribute.java
+++ b/src/main/javassist/bytecode/ParameterAnnotationsAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,16 +16,17 @@
package javassist.bytecode;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
-import java.io.IOException;
-import java.io.DataInputStream;
-import java.io.ByteArrayOutputStream;
import javassist.bytecode.AnnotationsAttribute.Copier;
import javassist.bytecode.AnnotationsAttribute.Parser;
import javassist.bytecode.AnnotationsAttribute.Renamer;
-import javassist.bytecode.annotation.*;
+import javassist.bytecode.annotation.Annotation;
+import javassist.bytecode.annotation.AnnotationsWriter;
/**
* A class representing <code>RuntimeVisibleAnnotations_attribute</code> and
@@ -103,7 +105,8 @@ public class ParameterAnnotationsAttribute extends AttributeInfo {
/**
* Copies this attribute and returns a new copy.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
Copier copier = new Copier(info, constPool, newCp, classnames);
try {
copier.parameters();
@@ -149,10 +152,8 @@ public class ParameterAnnotationsAttribute extends AttributeInfo {
ByteArrayOutputStream output = new ByteArrayOutputStream();
AnnotationsWriter writer = new AnnotationsWriter(output, constPool);
try {
- int n = params.length;
- writer.numParameters(n);
- for (int i = 0; i < n; ++i) {
- Annotation[] anno = params[i];
+ writer.numParameters(params.length);
+ for (Annotation[] anno:params) {
writer.numAnnotations(anno.length);
for (int j = 0; j < anno.length; ++j)
anno[j].write(writer);
@@ -171,13 +172,15 @@ public class ParameterAnnotationsAttribute extends AttributeInfo {
* @param oldname a JVM class name.
* @param newname a JVM class name.
*/
+ @Override
void renameClass(String oldname, String newname) {
- HashMap map = new HashMap();
+ Map<String,String> map = new HashMap<String,String>();
map.put(oldname, newname);
renameClass(map);
}
- void renameClass(Map classnames) {
+ @Override
+ void renameClass(Map<String,String> classnames) {
Renamer renamer = new Renamer(info, getConstPool(), classnames);
try {
renamer.parameters();
@@ -186,29 +189,23 @@ public class ParameterAnnotationsAttribute extends AttributeInfo {
}
}
- void getRefClasses(Map classnames) { renameClass(classnames); }
+ @Override
+ void getRefClasses(Map<String,String> classnames) { renameClass(classnames); }
/**
* Returns a string representation of this object.
*/
+ @Override
public String toString() {
Annotation[][] aa = getAnnotations();
StringBuilder sbuf = new StringBuilder();
- int k = 0;
- while (k < aa.length) {
- Annotation[] a = aa[k++];
- int i = 0;
- while (i < a.length) {
- sbuf.append(a[i++].toString());
- if (i != a.length)
- sbuf.append(" ");
- }
+ for (Annotation[] a : aa) {
+ for (Annotation i : a)
+ sbuf.append(i.toString()).append(" ");
- if (k != aa.length)
- sbuf.append(", ");
+ sbuf.append(", ");
}
- return sbuf.toString();
-
+ return sbuf.toString().replaceAll(" (?=,)|, $","");
}
}
diff --git a/src/main/javassist/bytecode/SignatureAttribute.java b/src/main/javassist/bytecode/SignatureAttribute.java
index 958e93f..1a8a62d 100644
--- a/src/main/javassist/bytecode/SignatureAttribute.java
+++ b/src/main/javassist/bytecode/SignatureAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -17,8 +18,11 @@ package javassist.bytecode;
import java.io.DataInputStream;
import java.io.IOException;
-import java.util.Map;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
import javassist.CtClass;
/**
@@ -37,7 +41,7 @@ public class SignatureAttribute extends AttributeInfo {
}
/**
- * Constructs a Signature attribute.
+ * Constructs a <code>Signature</code> attribute.
*
* @param cp a constant pool table.
* @param signature the signature represented by this attribute.
@@ -52,17 +56,18 @@ public class SignatureAttribute extends AttributeInfo {
}
/**
- * Returns the signature indicated by <code>signature_index</code>.
+ * Returns the generic signature indicated by <code>signature_index</code>.
*
* @see #toClassSignature(String)
* @see #toMethodSignature(String)
+ * @see #toFieldSignature(String)
*/
public String getSignature() {
return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0));
}
/**
- * Sets <code>signature_index</code> to the index of the given signature,
+ * Sets <code>signature_index</code> to the index of the given generic signature,
* which is added to a constant pool.
*
* @param sig new signature.
@@ -81,27 +86,30 @@ public class SignatureAttribute extends AttributeInfo {
* @param classnames pairs of replaced and substituted
* class names.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
return new SignatureAttribute(newCp, getSignature());
}
+ @Override
void renameClass(String oldname, String newname) {
String sig = renameClass(getSignature(), oldname, newname);
setSignature(sig);
}
- void renameClass(Map classnames) {
+ @Override
+ void renameClass(Map<String,String> classnames) {
String sig = renameClass(getSignature(), classnames);
setSignature(sig);
}
static String renameClass(String desc, String oldname, String newname) {
- Map map = new java.util.HashMap();
+ Map<String,String> map = new HashMap<String,String>();
map.put(oldname, newname);
return renameClass(desc, map);
}
- static String renameClass(String desc, Map map) {
+ static String renameClass(String desc, Map<String,String> map) {
if (map == null)
return desc;
@@ -130,7 +138,7 @@ public class SignatureAttribute extends AttributeInfo {
catch (IndexOutOfBoundsException e) { break; }
i = k + 1;
String name = nameBuf.toString();
- String name2 = (String)map.get(name);
+ String name2 = map.get(name);
if (name2 != null) {
newdesc.append(desc.substring(head, j));
newdesc.append('L');
@@ -142,15 +150,14 @@ public class SignatureAttribute extends AttributeInfo {
if (head == 0)
return desc;
- else {
- int len = desc.length();
- if (head < len)
- newdesc.append(desc.substring(head, len));
+ int len = desc.length();
+ if (head < len)
+ newdesc.append(desc.substring(head, len));
- return newdesc.toString();
- }
+ return newdesc.toString();
}
+ @SuppressWarnings("unused")
private static boolean isNamePart(int c) {
return c != ';' && c != '<';
}
@@ -162,10 +169,8 @@ public class SignatureAttribute extends AttributeInfo {
int i = s.indexOf(ch, position);
if (i < 0)
throw error(s);
- else {
- position = i + 1;
- return i;
- }
+ position = i + 1;
+ return i;
}
}
@@ -176,10 +181,27 @@ public class SignatureAttribute extends AttributeInfo {
TypeParameter[] params;
ClassType superClass;
ClassType[] interfaces;
- ClassSignature(TypeParameter[] p, ClassType s, ClassType[] i) {
- params = p;
- superClass = s;
- interfaces = i;
+
+ /**
+ * Constructs a class signature.
+ *
+ * @param params type parameters.
+ * @param superClass the super class.
+ * @param interfaces the interface types.
+ */
+ public ClassSignature(TypeParameter[] params, ClassType superClass, ClassType[] interfaces) {
+ this.params = params == null ? new TypeParameter[0] : params;
+ this.superClass = superClass == null ? ClassType.OBJECT : superClass;
+ this.interfaces = interfaces == null ? new ClassType[0] : interfaces;
+ }
+
+ /**
+ * Constructs a class signature.
+ *
+ * @param p type parameters.
+ */
+ public ClassSignature(TypeParameter[] p) {
+ this(p, null, null);
}
/**
@@ -206,6 +228,7 @@ public class SignatureAttribute extends AttributeInfo {
/**
* Returns the string representation.
*/
+ @Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
@@ -218,6 +241,26 @@ public class SignatureAttribute extends AttributeInfo {
return sbuf.toString();
}
+
+ /**
+ * Returns the encoded string representing the method type signature.
+ */
+ public String encode() {
+ StringBuffer sbuf = new StringBuffer();
+ if (params.length > 0) {
+ sbuf.append('<');
+ for (int i = 0; i < params.length; i++)
+ params[i].encode(sbuf);
+
+ sbuf.append('>');
+ }
+
+ superClass.encode(sbuf);
+ for (int i = 0; i < interfaces.length; i++)
+ interfaces[i].encode(sbuf);
+
+ return sbuf.toString();
+ }
}
/**
@@ -229,11 +272,20 @@ public class SignatureAttribute extends AttributeInfo {
Type retType;
ObjectType[] exceptions;
- MethodSignature(TypeParameter[] tp, Type[] p, Type ret, ObjectType[] ex) {
- typeParams = tp;
- params = p;
- retType = ret;
- exceptions = ex;
+ /**
+ * Constructs a method type signature. Any parameter can be null
+ * to represent <code>void</code> or nothing.
+ *
+ * @param tp type parameters.
+ * @param params parameter types.
+ * @param ret a return type, or null if the return type is <code>void</code>.
+ * @param ex exception types.
+ */
+ public MethodSignature(TypeParameter[] tp, Type[] params, Type ret, ObjectType[] ex) {
+ typeParams = tp == null ? new TypeParameter[0] : tp;
+ this.params = params == null ? new Type[0] : params;
+ retType = ret == null ? new BaseType("void") : ret;
+ exceptions = ex == null ? new ObjectType[0] : ex;
}
/**
@@ -266,6 +318,7 @@ public class SignatureAttribute extends AttributeInfo {
/**
* Returns the string representation.
*/
+ @Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
@@ -281,10 +334,40 @@ public class SignatureAttribute extends AttributeInfo {
return sbuf.toString();
}
+
+ /**
+ * Returns the encoded string representing the method type signature.
+ */
+ public String encode() {
+ StringBuffer sbuf = new StringBuffer();
+ if (typeParams.length > 0) {
+ sbuf.append('<');
+ for (int i = 0; i < typeParams.length; i++)
+ typeParams[i].encode(sbuf);
+
+ sbuf.append('>');
+ }
+
+ sbuf.append('(');
+ for (int i = 0; i < params.length; i++)
+ params[i].encode(sbuf);
+
+ sbuf.append(')');
+ retType.encode(sbuf);
+ if (exceptions.length > 0)
+ for (int i = 0; i < exceptions.length; i++) {
+ sbuf.append('^');
+ exceptions[i].encode(sbuf);
+ }
+
+ return sbuf.toString();
+ }
}
/**
* Formal type parameters.
+ *
+ * @see TypeArgument
*/
public static class TypeParameter {
String name;
@@ -298,6 +381,33 @@ public class SignatureAttribute extends AttributeInfo {
}
/**
+ * Constructs a <code>TypeParameter</code> representing a type parametre
+ * like <code>&lt;T extends ... &gt;</code>.
+ *
+ * @param name parameter name.
+ * @param superClass an upper bound class-type (or null).
+ * @param superInterfaces an upper bound interface-type (or null).
+ */
+ public TypeParameter(String name, ObjectType superClass, ObjectType[] superInterfaces) {
+ this.name = name;
+ this.superClass = superClass;
+ if (superInterfaces == null)
+ this.superInterfaces = new ObjectType[0];
+ else
+ this.superInterfaces = superInterfaces;
+ }
+
+ /**
+ * Constructs a <code>TypeParameter</code> representing a type parameter
+ * like <code>&lt;T&gt;</code>.
+ *
+ * @param name parameter name.
+ */
+ public TypeParameter(String name) {
+ this(name, null, null);
+ }
+
+ /**
* Returns the name of the type parameter.
*/
public String getName() {
@@ -306,8 +416,6 @@ public class SignatureAttribute extends AttributeInfo {
/**
* Returns the class bound of this parameter.
- *
- * @return null if the class bound is not specified.
*/
public ObjectType getClassBound() { return superClass; }
@@ -321,6 +429,7 @@ public class SignatureAttribute extends AttributeInfo {
/**
* Returns the string representation.
*/
+ @Override
public String toString() {
StringBuffer sbuf = new StringBuffer(getName());
if (superClass != null)
@@ -352,10 +461,27 @@ public class SignatureAttribute extends AttributeInfo {
sbuf.append('>');
}
+
+ void encode(StringBuffer sb) {
+ sb.append(name);
+ if (superClass == null)
+ sb.append(":Ljava/lang/Object;");
+ else {
+ sb.append(':');
+ superClass.encode(sb);
+ }
+
+ for (int i = 0; i < superInterfaces.length; i++) {
+ sb.append(':');
+ superInterfaces[i].encode(sb);
+ }
+ }
}
/**
* Type argument.
+ *
+ * @see TypeParameter
*/
public static class TypeArgument {
ObjectType arg;
@@ -367,6 +493,44 @@ public class SignatureAttribute extends AttributeInfo {
}
/**
+ * Constructs a <code>TypeArgument</code>.
+ * A type argument is <code>&lt;String&gt;</code>, <code>&lt;int[]&gt;</code>,
+ * or a type variable <code>&lt;T&gt;</code>, etc.
+ *
+ * @param t a class type, an array type, or a type variable.
+ */
+ public TypeArgument(ObjectType t) {
+ this(t, ' ');
+ }
+
+ /**
+ * Constructs a <code>TypeArgument</code> representing <code>&lt;?&gt;</code>.
+ */
+ public TypeArgument() {
+ this(null, '*');
+ }
+
+ /**
+ * A factory method constructing a <code>TypeArgument</code> with an upper bound.
+ * It represents <code>&lt;? extends ... &gt;</code>
+ *
+ * @param t an upper bound type.
+ */
+ public static TypeArgument subclassOf(ObjectType t) {
+ return new TypeArgument(t, '+');
+ }
+
+ /**
+ * A factory method constructing a <code>TypeArgument</code> with an lower bound.
+ * It represents <code>&lt;? super ... &gt;</code>
+ *
+ * @param t an lower bbound type.
+ */
+ public static TypeArgument superOf(ObjectType t) {
+ return new TypeArgument(t, '-');
+ }
+
+ /**
* Returns the kind of this type argument.
*
* @return <code>' '</code> (not-wildcard), <code>'*'</code> (wildcard), <code>'+'</code> (wildcard with
@@ -392,6 +556,7 @@ public class SignatureAttribute extends AttributeInfo {
/**
* Returns the string representation.
*/
+ @Override
public String toString() {
if (wildcard == '*')
return "?";
@@ -404,12 +569,27 @@ public class SignatureAttribute extends AttributeInfo {
else
return "? super " + type;
}
+
+ static void encode(StringBuffer sb, TypeArgument[] args) {
+ sb.append('<');
+ for (int i = 0; i < args.length; i++) {
+ TypeArgument ta = args[i];
+ if (ta.isWildcard())
+ sb.append(ta.wildcard);
+
+ if (ta.getType() != null)
+ ta.getType().encode(sb);
+ }
+
+ sb.append('>');
+ }
}
/**
* Primitive types and object types.
*/
public static abstract class Type {
+ abstract void encode(StringBuffer sb);
static void toString(StringBuffer sbuf, Type[] ts) {
for (int i = 0; i < ts.length; i++) {
if (i > 0)
@@ -418,6 +598,13 @@ public class SignatureAttribute extends AttributeInfo {
sbuf.append(ts[i]);
}
}
+
+ /**
+ * Returns the type name in the JVM internal style.
+ * For example, if the type is a nested class {@code foo.Bar.Baz},
+ * then {@code foo.Bar$Baz} is returned.
+ */
+ public String jvmTypeName() { return toString(); }
}
/**
@@ -428,6 +615,15 @@ public class SignatureAttribute extends AttributeInfo {
BaseType(char c) { descriptor = c; }
/**
+ * Constructs a <code>BaseType</code>.
+ *
+ * @param typeName <code>void</code>, <code>int</code>, ...
+ */
+ public BaseType(String typeName) {
+ this(Descriptor.of(typeName).charAt(0));
+ }
+
+ /**
* Returns the descriptor representing this primitive type.
*
* @see javassist.bytecode.Descriptor
@@ -445,15 +641,31 @@ public class SignatureAttribute extends AttributeInfo {
/**
* Returns the string representation.
*/
+ @Override
public String toString() {
return Descriptor.toClassName(Character.toString(descriptor));
}
+
+ @Override
+ void encode(StringBuffer sb) {
+ sb.append(descriptor);
+ }
}
/**
* Class types, array types, and type variables.
+ * This class is also used for representing a field type.
*/
- public static abstract class ObjectType extends Type {}
+ public static abstract class ObjectType extends Type {
+ /**
+ * Returns the encoded string representing the object type signature.
+ */
+ public String encode() {
+ StringBuffer sb = new StringBuffer();
+ encode(sb);
+ return sb.toString();
+ }
+ }
/**
* Class types.
@@ -466,8 +678,7 @@ public class SignatureAttribute extends AttributeInfo {
TypeArgument[] targs, ClassType parent) {
if (parent == null)
return new ClassType(s, b, e, targs);
- else
- return new NestedClassType(s, b, e, targs, parent);
+ return new NestedClassType(s, b, e, targs, parent);
}
ClassType(String signature, int begin, int end, TypeArgument[] targs) {
@@ -476,6 +687,33 @@ public class SignatureAttribute extends AttributeInfo {
}
/**
+ * A class type representing <code>java.lang.Object</code>.
+ */
+ public static ClassType OBJECT = new ClassType("java.lang.Object", null);
+
+ /**
+ * Constructs a <code>ClassType</code>. It represents
+ * the name of a non-nested class.
+ *
+ * @param className a fully qualified class name.
+ * @param args type arguments or null.
+ */
+ public ClassType(String className, TypeArgument[] args) {
+ name = className;
+ arguments = args;
+ }
+
+ /**
+ * Constructs a <code>ClassType</code>. It represents
+ * the name of a non-nested class.
+ *
+ * @param className a fully qualified class name.
+ */
+ public ClassType(String className) {
+ this(className, null);
+ }
+
+ /**
* Returns the class name.
*/
public String getName() {
@@ -500,12 +738,17 @@ public class SignatureAttribute extends AttributeInfo {
/**
* Returns the string representation.
*/
+ @Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
ClassType parent = getDeclaringClass();
if (parent != null)
sbuf.append(parent.toString()).append('.');
+ return toString2(sbuf);
+ }
+
+ private String toString2(StringBuffer sbuf) {
sbuf.append(name);
if (arguments != null) {
sbuf.append('<');
@@ -522,6 +765,40 @@ public class SignatureAttribute extends AttributeInfo {
return sbuf.toString();
}
+
+ /**
+ * Returns the type name in the JVM internal style.
+ * For example, if the type is a nested class {@code foo.Bar.Baz},
+ * then {@code foo.Bar$Baz} is returned.
+ */
+ @Override
+ public String jvmTypeName() {
+ StringBuffer sbuf = new StringBuffer();
+ ClassType parent = getDeclaringClass();
+ if (parent != null)
+ sbuf.append(parent.jvmTypeName()).append('$');
+
+ return toString2(sbuf);
+ }
+
+ @Override
+ void encode(StringBuffer sb) {
+ sb.append('L');
+ encode2(sb);
+ sb.append(';');
+ }
+
+ void encode2(StringBuffer sb) {
+ ClassType parent = getDeclaringClass();
+ if (parent != null) {
+ parent.encode2(sb);
+ sb.append('$');
+ }
+
+ sb.append(name.replace('.', '/'));
+ if (arguments != null)
+ TypeArgument.encode(sb, arguments);
+ }
}
/**
@@ -536,9 +813,23 @@ public class SignatureAttribute extends AttributeInfo {
}
/**
+ * Constructs a <code>NestedClassType</code>.
+ *
+ * @param parent the class surrounding this class type.
+ * @param className a simple class name. It does not include
+ * a package name or a parent's class name.
+ * @param args type parameters or null.
+ */
+ public NestedClassType(ClassType parent, String className, TypeArgument[] args) {
+ super(className, args);
+ this.parent = parent;
+ }
+
+ /**
* Returns the class that declares this nested class.
* This nested class is a member of that declaring class.
*/
+ @Override
public ClassType getDeclaringClass() { return parent; }
}
@@ -549,6 +840,12 @@ public class SignatureAttribute extends AttributeInfo {
int dim;
Type componentType;
+ /**
+ * Constructs an <code>ArrayType</code>.
+ *
+ * @param d dimension.
+ * @param comp the component type.
+ */
public ArrayType(int d, Type comp) {
dim = d;
componentType = comp;
@@ -569,6 +866,7 @@ public class SignatureAttribute extends AttributeInfo {
/**
* Returns the string representation.
*/
+ @Override
public String toString() {
StringBuffer sbuf = new StringBuffer(componentType.toString());
for (int i = 0; i < dim; i++)
@@ -576,6 +874,14 @@ public class SignatureAttribute extends AttributeInfo {
return sbuf.toString();
}
+
+ @Override
+ void encode(StringBuffer sb) {
+ for (int i = 0; i < dim; i++)
+ sb.append('[');
+
+ componentType.encode(sb);
+ }
}
/**
@@ -589,6 +895,15 @@ public class SignatureAttribute extends AttributeInfo {
}
/**
+ * Constructs a <code>TypeVariable</code>.
+ *
+ * @param name the name of a type variable.
+ */
+ public TypeVariable(String name) {
+ this.name = name;
+ }
+
+ /**
* Returns the variable name.
*/
public String getName() {
@@ -598,16 +913,26 @@ public class SignatureAttribute extends AttributeInfo {
/**
* Returns the string representation.
*/
+ @Override
public String toString() {
return name;
}
+
+ @Override
+ void encode(StringBuffer sb) {
+ sb.append('T').append(name).append(';');
+ }
}
/**
* Parses the given signature string as a class signature.
*
- * @param sig the signature.
+ * @param sig the signature obtained from the <code>SignatureAttribute</code>
+ * of a <code>ClassFile</code>.
+ * @return a tree-like data structure representing a class signature. It provides
+ * convenient accessor methods.
* @throws BadBytecode thrown when a syntactical error is found.
+ * @see #getSignature()
* @since 3.5
*/
public static ClassSignature toClassSignature(String sig) throws BadBytecode {
@@ -622,8 +947,12 @@ public class SignatureAttribute extends AttributeInfo {
/**
* Parses the given signature string as a method type signature.
*
- * @param sig the signature.
+ * @param sig the signature obtained from the <code>SignatureAttribute</code>
+ * of a <code>MethodInfo</code>.
+ * @return @return a tree-like data structure representing a method signature. It provides
+ * convenient accessor methods.
* @throws BadBytecode thrown when a syntactical error is found.
+ * @see #getSignature()
* @since 3.5
*/
public static MethodSignature toMethodSignature(String sig) throws BadBytecode {
@@ -638,9 +967,11 @@ public class SignatureAttribute extends AttributeInfo {
/**
* Parses the given signature string as a field type signature.
*
- * @param sig the signature string.
+ * @param sig the signature string obtained from the <code>SignatureAttribute</code>
+ * of a <code>FieldInfo</code>.
* @return the field type signature.
* @throws BadBytecode thrown when a syntactical error is found.
+ * @see #getSignature()
* @since 3.5
*/
public static ObjectType toFieldSignature(String sig) throws BadBytecode {
@@ -652,6 +983,23 @@ public class SignatureAttribute extends AttributeInfo {
}
}
+ /**
+ * Parses the given signature string as a type signature.
+ * The type signature is either the field type signature or a base type
+ * descriptor including <code>void</code> type.
+ *
+ * @throws BadBytecode thrown when a syntactical error is found.
+ * @since 3.18
+ */
+ public static Type toTypeSignature(String sig) throws BadBytecode {
+ try {
+ return parseType(sig, new Cursor());
+ }
+ catch (IndexOutOfBoundsException e) {
+ throw error(sig);
+ }
+ }
+
private static ClassSignature parseSig(String sig)
throws BadBytecode, IndexOutOfBoundsException
{
@@ -659,12 +1007,12 @@ public class SignatureAttribute extends AttributeInfo {
TypeParameter[] tp = parseTypeParams(sig, cur);
ClassType superClass = parseClassType(sig, cur);
int sigLen = sig.length();
- ArrayList ifArray = new ArrayList();
+ List<ClassType> ifArray = new ArrayList<ClassType>();
while (cur.position < sigLen && sig.charAt(cur.position) == 'L')
ifArray.add(parseClassType(sig, cur));
ClassType[] ifs
- = (ClassType[])ifArray.toArray(new ClassType[ifArray.size()]);
+ = ifArray.toArray(new ClassType[ifArray.size()]);
return new ClassSignature(tp, superClass, ifs);
}
@@ -676,7 +1024,7 @@ public class SignatureAttribute extends AttributeInfo {
if (sig.charAt(cur.position++) != '(')
throw error(sig);
- ArrayList params = new ArrayList();
+ List<Type> params = new ArrayList<Type>();
while (sig.charAt(cur.position) != ')') {
Type t = parseType(sig, cur);
params.add(t);
@@ -685,7 +1033,7 @@ public class SignatureAttribute extends AttributeInfo {
cur.position++;
Type ret = parseType(sig, cur);
int sigLen = sig.length();
- ArrayList exceptions = new ArrayList();
+ List<ObjectType> exceptions = new ArrayList<ObjectType>();
while (cur.position < sigLen && sig.charAt(cur.position) == '^') {
cur.position++;
ObjectType t = parseObjectType(sig, cur, false);
@@ -695,22 +1043,22 @@ public class SignatureAttribute extends AttributeInfo {
exceptions.add(t);
}
- Type[] p = (Type[])params.toArray(new Type[params.size()]);
- ObjectType[] ex = (ObjectType[])exceptions.toArray(new ObjectType[exceptions.size()]);
+ Type[] p = params.toArray(new Type[params.size()]);
+ ObjectType[] ex = exceptions.toArray(new ObjectType[exceptions.size()]);
return new MethodSignature(tp, p, ret, ex);
}
private static TypeParameter[] parseTypeParams(String sig, Cursor cur)
throws BadBytecode
{
- ArrayList typeParam = new ArrayList();
+ List<TypeParameter> typeParam = new ArrayList<TypeParameter>();
if (sig.charAt(cur.position) == '<') {
cur.position++;
while (sig.charAt(cur.position) != '>') {
int nameBegin = cur.position;
int nameEnd = cur.indexOf(sig, ':');
ObjectType classBound = parseObjectType(sig, cur, true);
- ArrayList ifBound = new ArrayList();
+ List<ObjectType> ifBound = new ArrayList<ObjectType>();
while (sig.charAt(cur.position) == ':') {
cur.position++;
ObjectType t = parseObjectType(sig, cur, false);
@@ -718,14 +1066,14 @@ public class SignatureAttribute extends AttributeInfo {
}
TypeParameter p = new TypeParameter(sig, nameBegin, nameEnd,
- classBound, (ObjectType[])ifBound.toArray(new ObjectType[ifBound.size()]));
+ classBound, ifBound.toArray(new ObjectType[ifBound.size()]));
typeParam.add(p);
}
cur.position++;
}
- return (TypeParameter[])typeParam.toArray(new TypeParameter[typeParam.size()]);
+ return typeParam.toArray(new TypeParameter[typeParam.size()]);
}
private static ObjectType parseObjectType(String sig, Cursor c, boolean dontThrow)
@@ -744,8 +1092,7 @@ public class SignatureAttribute extends AttributeInfo {
default :
if (dontThrow)
return null;
- else
- throw error(sig);
+ throw error(sig);
}
}
@@ -754,8 +1101,7 @@ public class SignatureAttribute extends AttributeInfo {
{
if (sig.charAt(c.position) == 'L')
return parseClassType2(sig, c, null);
- else
- throw error(sig);
+ throw error(sig);
}
private static ClassType parseClassType2(String sig, Cursor c, ClassType parent)
@@ -776,16 +1122,15 @@ public class SignatureAttribute extends AttributeInfo {
targs = null;
ClassType thisClass = ClassType.make(sig, start, end, targs, parent);
- if (t == '$') {
+ if (t == '$' || t == '.') {
c.position--;
return parseClassType2(sig, c, thisClass);
}
- else
- return thisClass;
+ return thisClass;
}
private static TypeArgument[] parseTypeArgs(String sig, Cursor c) throws BadBytecode {
- ArrayList args = new ArrayList();
+ List<TypeArgument> args = new ArrayList<TypeArgument>();
char t;
while ((t = sig.charAt(c.position++)) != '>') {
TypeArgument ta;
@@ -803,7 +1148,7 @@ public class SignatureAttribute extends AttributeInfo {
args.add(ta);
}
- return (TypeArgument[])args.toArray(new TypeArgument[args.size()]);
+ return args.toArray(new TypeArgument[args.size()]);
}
private static ObjectType parseArray(String sig, Cursor c) throws BadBytecode {
diff --git a/src/main/javassist/bytecode/SourceFileAttribute.java b/src/main/javassist/bytecode/SourceFileAttribute.java
index c104eb7..77d32e5 100644
--- a/src/main/javassist/bytecode/SourceFileAttribute.java
+++ b/src/main/javassist/bytecode/SourceFileAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -64,7 +65,8 @@ public class SourceFileAttribute extends AttributeInfo {
* @param classnames pairs of replaced and substituted
* class names.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
return new SourceFileAttribute(newCp, getFileName());
}
}
diff --git a/src/main/javassist/bytecode/StackMap.java b/src/main/javassist/bytecode/StackMap.java
index ac0582e..96a8449 100644
--- a/src/main/javassist/bytecode/StackMap.java
+++ b/src/main/javassist/bytecode/StackMap.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -21,9 +22,6 @@ import java.io.IOException;
import java.util.Map;
import javassist.CannotCompileException;
-import javassist.bytecode.StackMapTable.InsertLocal;
-import javassist.bytecode.StackMapTable.NewRemover;
-import javassist.bytecode.StackMapTable.Shifter;
/**
* Another <code>stack_map</code> attribute defined in CLDC 1.1 for J2ME.
@@ -115,7 +113,8 @@ public class StackMap extends AttributeInfo {
/**
* Makes a copy.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
Copier copier = new Copier(this, newCp, classnames);
copier.visit();
return copier.getStackMap();
@@ -223,42 +222,47 @@ public class StackMap extends AttributeInfo {
static class Copier extends Walker {
byte[] dest;
ConstPool srcCp, destCp;
- Map classnames;
+ Map<String,String> classnames;
- Copier(StackMap map, ConstPool newCp, Map classnames) {
+ Copier(StackMap map, ConstPool newCp, Map<String,String> classnames) {
super(map);
srcCp = map.getConstPool();
dest = new byte[info.length];
destCp = newCp;
this.classnames = classnames;
}
-
+ @Override
public void visit() {
int num = ByteArray.readU16bit(info, 0);
ByteArray.write16bit(num, dest, 0);
super.visit();
}
+ @Override
public int locals(int pos, int offset, int num) {
ByteArray.write16bit(offset, dest, pos - 4);
return super.locals(pos, offset, num);
}
+ @Override
public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
ByteArray.write16bit(num, dest, pos - 2);
return super.typeInfoArray(pos, offset, num, isLocals);
}
+ @Override
public void typeInfo(int pos, byte tag) {
dest[pos] = tag;
}
+ @Override
public void objectVariable(int pos, int clazz) {
dest[pos] = OBJECT;
int newClazz = srcCp.copy(clazz, destCp, classnames);
ByteArray.write16bit(newClazz, dest, pos + 1);
}
+ @Override
public void uninitialized(int pos, int offset) {
dest[pos] = UNINIT;
ByteArray.write16bit(offset, dest, pos + 1);
@@ -304,30 +308,36 @@ public class StackMap extends AttributeInfo {
return writer.toByteArray();
}
+ @Override
public void visit() {
int num = ByteArray.readU16bit(info, 0);
writer.write16bit(num);
super.visit();
}
+ @Override
public int locals(int pos, int offset, int num) {
writer.write16bit(offset);
return super.locals(pos, offset, num);
}
+ @Override
public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
writer.write16bit(num);
return super.typeInfoArray(pos, offset, num, isLocals);
}
+ @Override
public void typeInfo(int pos, byte tag) {
writer.writeVerifyTypeInfo(tag, 0);
}
+ @Override
public void objectVariable(int pos, int clazz) {
writer.writeVerifyTypeInfo(OBJECT, clazz);
}
+ @Override
public void uninitialized(int pos, int offset) {
writer.writeVerifyTypeInfo(UNINIT, offset);
}
@@ -344,6 +354,7 @@ public class StackMap extends AttributeInfo {
this.varData = varData;
}
+ @Override
public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
if (!isLocals || num < varIndex)
return super.typeInfoArray(pos, offset, num, isLocals);
@@ -389,12 +400,46 @@ public class StackMap extends AttributeInfo {
this.exclusive = exclusive;
}
+ @Override
public int locals(int pos, int offset, int num) {
if (exclusive ? where <= offset : where < offset)
ByteArray.write16bit(offset + gap, info, pos - 4);
return super.locals(pos, offset, num);
}
+
+ @Override
+ public void uninitialized(int pos, int offset) {
+ if (where <= offset)
+ ByteArray.write16bit(offset + gap, info, pos + 1);
+ }
+ }
+
+ /**
+ * @see CodeIterator.Switcher#adjustOffsets(int, int)
+ */
+ void shiftForSwitch(int where, int gapSize) throws BadBytecode {
+ new SwitchShifter(this, where, gapSize).visit();
+ }
+
+ static class SwitchShifter extends Walker {
+ private int where, gap;
+
+ public SwitchShifter(StackMap smt, int where, int gap) {
+ super(smt);
+ this.where = where;
+ this.gap = gap;
+ }
+
+ @Override
+ public int locals(int pos, int offset, int num) {
+ if (where == pos + offset)
+ ByteArray.write16bit(offset - gap, info, pos - 4);
+ else if (where == pos)
+ ByteArray.write16bit(offset + gap, info, pos - 4);
+
+ return super.locals(pos, offset, num);
+ }
}
/**
@@ -419,6 +464,7 @@ public class StackMap extends AttributeInfo {
posOfNew = where;
}
+ @Override
public int stack(int pos, int offset, int num) {
return stackTypeInfoArray(pos, offset, num);
}
@@ -487,6 +533,7 @@ public class StackMap extends AttributeInfo {
visit();
}
+ @Override
public int locals(int pos, int offset, int num) {
writer.println(" * offset " + offset);
return super.locals(pos, offset, num);
diff --git a/src/main/javassist/bytecode/StackMapTable.java b/src/main/javassist/bytecode/StackMapTable.java
index f3cea6f..9b8e5fa 100644
--- a/src/main/javassist/bytecode/StackMapTable.java
+++ b/src/main/javassist/bytecode/StackMapTable.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,19 +16,20 @@
package javassist.bytecode;
+import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintWriter;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.Map;
+
import javassist.CannotCompileException;
/**
* <code>stack_map</code> attribute.
*
* <p>This is an entry in the attributes table of a Code attribute.
- * It was introduced by J2SE 6 for the process of verification by
+ * It was introduced by J2SE 6 for the verification by
* typechecking.
*
* @see StackMap
@@ -61,12 +63,13 @@ public class StackMapTable extends AttributeInfo {
* <code>RuntimeCopyException</code>.
*
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames)
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames)
throws RuntimeCopyException
{
try {
return new StackMapTable(newCp,
- new Copier(this.constPool, info, newCp).doit());
+ new Copier(this.constPool, info, newCp, classnames).doit());
}
catch (BadBytecode e) {
throw new RuntimeCopyException("bad bytecode. fatal?");
@@ -78,6 +81,9 @@ public class StackMapTable extends AttributeInfo {
* in <code>StackMapTable</code>.
*/
public static class RuntimeCopyException extends RuntimeException {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
/**
* Constructs an exception.
*/
@@ -86,6 +92,7 @@ public class StackMapTable extends AttributeInfo {
}
}
+ @Override
void write(DataOutputStream out) throws IOException {
super.write(out);
}
@@ -243,6 +250,7 @@ public class StackMapTable extends AttributeInfo {
int data = 0;
if (tag == OBJECT || tag == UNINIT) {
data = ByteArray.readU16bit(info, pos + 2);
+ objectOrUninitialized(tag, data, pos + 2);
pos += 2;
}
@@ -270,7 +278,7 @@ public class StackMapTable extends AttributeInfo {
*
* @param pos the position.
* @param offsetDelta
- * @param k the <cod>k</code> last locals are absent.
+ * @param k the <code>k</code> last locals are absent.
*/
public void chopFrame(int pos, int offsetDelta, int k) throws BadBytecode {}
@@ -285,6 +293,7 @@ public class StackMapTable extends AttributeInfo {
tags[i] = tag;
if (tag == OBJECT || tag == UNINIT) {
data[i] = ByteArray.readU16bit(info, p + 1);
+ objectOrUninitialized(tag, data[i], p + 1);
p += 3;
}
else {
@@ -304,7 +313,7 @@ public class StackMapTable extends AttributeInfo {
* @param offsetDelta
* @param tags <code>locals[i].tag</code>.
* @param data <code>locals[i].cpool_index</code>
- * or <cod>locals[i].offset</code>.
+ * or <code>locals[i].offset</code>.
*/
public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data)
throws BadBytecode {}
@@ -335,8 +344,8 @@ public class StackMapTable extends AttributeInfo {
* @param stackData <code>stack[i].cpool_index</code>
* or <code>stack[i].offset</code>
*/
- public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
- int[] stackTags, int[] stackData)
+ public void fullFrame(int pos, int offsetDelta, int[] localTags,
+ int[] localData, int[] stackTags, int[] stackData)
throws BadBytecode {}
private int verifyTypeInfo(int pos, int n, int[] tags, int[] data) {
@@ -345,12 +354,23 @@ public class StackMapTable extends AttributeInfo {
tags[i] = tag;
if (tag == OBJECT || tag == UNINIT) {
data[i] = ByteArray.readU16bit(info, pos);
+ objectOrUninitialized(tag, data[i], pos);
pos += 2;
}
}
return pos;
}
+
+ /**
+ * Invoked if <code>Object_variable_info</code>
+ * or <code>Uninitialized_variable_info</code> is visited.
+ *
+ * @param tag <code>OBJECT</code> or <code>UNINIT</code>.
+ * @param data the value of <code>cpool_index</code> or <code>offset</code>.
+ * @param pos the position of <code>cpool_index</code> or <code>offset</code>.
+ */
+ public void objectOrUninitialized(int tag, int data, int pos) {}
}
static class SimpleCopy extends Walker {
@@ -366,22 +386,27 @@ public class StackMapTable extends AttributeInfo {
return writer.toByteArray();
}
+ @Override
public void sameFrame(int pos, int offsetDelta) {
writer.sameFrame(offsetDelta);
}
+ @Override
public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) {
writer.sameLocals(offsetDelta, stackTag, copyData(stackTag, stackData));
}
+ @Override
public void chopFrame(int pos, int offsetDelta, int k) {
writer.chopFrame(offsetDelta, k);
}
+ @Override
public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) {
writer.appendFrame(offsetDelta, tags, copyData(tags, data));
}
+ @Override
public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
int[] stackTags, int[] stackData) {
writer.fullFrame(offsetDelta, localTags, copyData(localTags, localData),
@@ -399,25 +424,28 @@ public class StackMapTable extends AttributeInfo {
static class Copier extends SimpleCopy {
private ConstPool srcPool, destPool;
+ private Map<String,String> classnames;
- public Copier(ConstPool src, byte[] data, ConstPool dest) {
+ public Copier(ConstPool src, byte[] data, ConstPool dest, Map<String,String> names) {
super(data);
srcPool = src;
destPool = dest;
+ classnames = names;
}
+ @Override
protected int copyData(int tag, int data) {
if (tag == OBJECT)
- return srcPool.copy(data, destPool, null);
- else
- return data;
+ return srcPool.copy(data, destPool, classnames);
+ return data;
}
+ @Override
protected int[] copyData(int[] tags, int[] data) {
int[] newData = new int[data.length];
for (int i = 0; i < data.length; i++)
if (tags[i] == OBJECT)
- newData[i] = srcPool.copy(data[i], destPool, null);
+ newData[i] = srcPool.copy(data[i], destPool, classnames);
else
newData[i] = data[i];
@@ -488,6 +516,7 @@ public class StackMapTable extends AttributeInfo {
this.varData = varData;
}
+ @Override
public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
int[] stackTags, int[] stackData) {
int len = localTags.length;
@@ -578,7 +607,7 @@ public class StackMapTable extends AttributeInfo {
* @param tag <code>stack[0].tag</code>.
* @param data <code>stack[0].cpool_index</code>
* if the tag is <code>OBJECT</code>,
- * or <cod>stack[0].offset</code>
+ * or <code>stack[0].offset</code>
* if the tag is <code>UNINIT</code>.
* Otherwise, this parameter is not used.
*/
@@ -614,7 +643,7 @@ public class StackMapTable extends AttributeInfo {
* either 1, 2, or 3.
* @param data <code>locals[].cpool_index</code>
* if the tag is <code>OBJECT</code>,
- * or <cod>locals[].offset</code>
+ * or <code>locals[].offset</code>
* if the tag is <code>UNINIT</code>.
* Otherwise, this parameter is not used.
*/
@@ -636,13 +665,13 @@ public class StackMapTable extends AttributeInfo {
* @param localTags <code>locals[].tag</code>.
* @param localData <code>locals[].cpool_index</code>
* if the tag is <code>OBJECT</code>,
- * or <cod>locals[].offset</code>
+ * or <code>locals[].offset</code>
* if the tag is <code>UNINIT</code>.
* Otherwise, this parameter is not used.
* @param stackTags <code>stack[].tag</code>.
* @param stackData <code>stack[].cpool_index</code>
* if the tag is <code>OBJECT</code>,
- * or <cod>stack[].offset</code>
+ * or <code>stack[].offset</code>
* if the tag is <code>UNINIT</code>.
* Otherwise, this parameter is not used.
*/
@@ -712,22 +741,26 @@ public class StackMapTable extends AttributeInfo {
offset = -1;
}
+ @Override
public void sameFrame(int pos, int offsetDelta) {
offset += offsetDelta + 1;
writer.println(offset + " same frame: " + offsetDelta);
}
+ @Override
public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) {
offset += offsetDelta + 1;
writer.println(offset + " same locals: " + offsetDelta);
printTypeInfo(stackTag, stackData);
}
+ @Override
public void chopFrame(int pos, int offsetDelta, int k) {
offset += offsetDelta + 1;
writer.println(offset + " chop frame: " + offsetDelta + ", " + k + " last locals");
}
+ @Override
public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) {
offset += offsetDelta + 1;
writer.println(offset + " append frame: " + offsetDelta);
@@ -735,6 +768,7 @@ public class StackMapTable extends AttributeInfo {
printTypeInfo(tags[i], data[i]);
}
+ @Override
public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
int[] stackTags, int[] stackData) {
offset += offsetDelta + 1;
@@ -788,15 +822,33 @@ public class StackMapTable extends AttributeInfo {
void shiftPc(int where, int gapSize, boolean exclusive)
throws BadBytecode
{
+ new OffsetShifter(this, where, gapSize).parse();
new Shifter(this, where, gapSize, exclusive).doit();
}
+ static class OffsetShifter extends Walker {
+ int where, gap;
+
+ public OffsetShifter(StackMapTable smt, int where, int gap) {
+ super(smt);
+ this.where = where;
+ this.gap = gap;
+ }
+
+ @Override
+ public void objectOrUninitialized(int tag, int data, int pos) {
+ if (tag == UNINIT)
+ if (where <= data)
+ ByteArray.write16bit(data + gap, info, pos);
+ }
+ }
+
static class Shifter extends Walker {
private StackMapTable stackMap;
- private int where, gap;
- private int position;
- private byte[] updatedInfo;
- private boolean exclusive;
+ int where, gap;
+ int position;
+ byte[] updatedInfo;
+ boolean exclusive;
public Shifter(StackMapTable smt, int where, int gap, boolean exclusive) {
super(smt);
@@ -814,15 +866,17 @@ public class StackMapTable extends AttributeInfo {
stackMap.set(updatedInfo);
}
+ @Override
public void sameFrame(int pos, int offsetDelta) {
update(pos, offsetDelta, 0, 251);
}
+ @Override
public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) {
update(pos, offsetDelta, 64, 247);
}
- private void update(int pos, int offsetDelta, int base, int entry) {
+ void update(int pos, int offsetDelta, int base, int entry) {
int oldPos = position;
position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1);
boolean match;
@@ -847,7 +901,7 @@ public class StackMapTable extends AttributeInfo {
}
}
- private static byte[] insertGap(byte[] info, int where, int gap) {
+ static byte[] insertGap(byte[] info, int where, int gap) {
int len = info.length;
byte[] newinfo = new byte[len + gap];
for (int i = 0; i < len; i++)
@@ -856,20 +910,23 @@ public class StackMapTable extends AttributeInfo {
return newinfo;
}
+ @Override
public void chopFrame(int pos, int offsetDelta, int k) {
update(pos, offsetDelta);
}
+ @Override
public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) {
update(pos, offsetDelta);
}
+ @Override
public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
int[] stackTags, int[] stackData) {
update(pos, offsetDelta);
}
- private void update(int pos, int offsetDelta) {
+ void update(int pos, int offsetDelta) {
int oldPos = position;
position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1);
boolean match;
@@ -887,6 +944,75 @@ public class StackMapTable extends AttributeInfo {
}
/**
+ * @see CodeIterator.Switcher#adjustOffsets(int, int)
+ */
+ void shiftForSwitch(int where, int gapSize) throws BadBytecode {
+ new SwitchShifter(this, where, gapSize).doit();
+ }
+
+ static class SwitchShifter extends Shifter {
+ SwitchShifter(StackMapTable smt, int where, int gap) {
+ super(smt, where, gap, false);
+ }
+
+ @Override
+ void update(int pos, int offsetDelta, int base, int entry) {
+ int oldPos = position;
+ position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1);
+ int newDelta = offsetDelta;
+ if (where == position)
+ newDelta = offsetDelta - gap;
+ else if (where == oldPos)
+ newDelta = offsetDelta + gap;
+ else
+ return;
+
+ if (offsetDelta < 64)
+ if (newDelta < 64)
+ info[pos] = (byte)(newDelta + base);
+ else {
+ byte[] newinfo = insertGap(info, pos, 2);
+ newinfo[pos] = (byte)entry;
+ ByteArray.write16bit(newDelta, newinfo, pos + 1);
+ updatedInfo = newinfo;
+ }
+ else
+ if (newDelta < 64) {
+ byte[] newinfo = deleteGap(info, pos, 2);
+ newinfo[pos] = (byte)(newDelta + base);
+ updatedInfo = newinfo;
+ }
+ else
+ ByteArray.write16bit(newDelta, info, pos + 1);
+ }
+
+ static byte[] deleteGap(byte[] info, int where, int gap) {
+ where += gap;
+ int len = info.length;
+ byte[] newinfo = new byte[len - gap];
+ for (int i = 0; i < len; i++)
+ newinfo[i - (i < where ? 0 : gap)] = info[i];
+
+ return newinfo;
+ }
+
+ @Override
+ void update(int pos, int offsetDelta) {
+ int oldPos = position;
+ position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1);
+ int newDelta = offsetDelta;
+ if (where == position)
+ newDelta = offsetDelta - gap;
+ else if (where == oldPos)
+ newDelta = offsetDelta + gap;
+ else
+ return;
+
+ ByteArray.write16bit(newDelta, info, pos + 1);
+ }
+ }
+
+ /**
* Undocumented method. Do not use; internal-use only.
*
* <p>This method is for javassist.convert.TransformNew.
@@ -913,6 +1039,7 @@ public class StackMapTable extends AttributeInfo {
posOfNew = pos;
}
+ @Override
public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) {
if (stackTag == UNINIT && stackData == posOfNew)
super.sameFrame(pos, offsetDelta);
@@ -920,6 +1047,7 @@ public class StackMapTable extends AttributeInfo {
super.sameLocals(pos, offsetDelta, stackTag, stackData);
}
+ @Override
public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
int[] stackTags, int[] stackData) {
int n = stackTags.length - 1;
diff --git a/src/main/javassist/bytecode/SyntheticAttribute.java b/src/main/javassist/bytecode/SyntheticAttribute.java
index 8cc697d..be6b35e 100644
--- a/src/main/javassist/bytecode/SyntheticAttribute.java
+++ b/src/main/javassist/bytecode/SyntheticAttribute.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -49,7 +50,8 @@ public class SyntheticAttribute extends AttributeInfo {
* @param newCp the constant pool table used by the new copy.
* @param classnames should be null.
*/
- public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
return new SyntheticAttribute(newCp);
}
}
diff --git a/src/main/javassist/bytecode/TypeAnnotationsAttribute.java b/src/main/javassist/bytecode/TypeAnnotationsAttribute.java
new file mode 100644
index 0000000..86fea72
--- /dev/null
+++ b/src/main/javassist/bytecode/TypeAnnotationsAttribute.java
@@ -0,0 +1,381 @@
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javassist.bytecode.annotation.TypeAnnotationsWriter;
+
+/**
+ * A class representing
+ * {@code RuntimeVisibleTypeAnnotations} attribute and
+ * {@code RuntimeInvisibleTypeAnnotations} attribute.
+ *
+ * @since 3.19
+ */
+public class TypeAnnotationsAttribute extends AttributeInfo {
+ /**
+ * The name of the {@code RuntimeVisibleTypeAnnotations} attribute.
+ */
+ public static final String visibleTag = "RuntimeVisibleTypeAnnotations";
+
+ /**
+ * The name of the {@code RuntimeInvisibleTypeAnnotations} attribute.
+ */
+ public static final String invisibleTag = "RuntimeInvisibleTypeAnnotations";
+
+ /**
+ * Constructs a <code>Runtime(In)VisibleTypeAnnotations_attribute</code>.
+ *
+ * @param cp constant pool
+ * @param attrname attribute name (<code>visibleTag</code> or
+ * <code>invisibleTag</code>).
+ * @param info the contents of this attribute. It does not
+ * include <code>attribute_name_index</code> or
+ * <code>attribute_length</code>.
+ */
+ public TypeAnnotationsAttribute(ConstPool cp, String attrname, byte[] info) {
+ super(cp, attrname, info);
+ }
+
+ /**
+ * @param n the attribute name.
+ */
+ TypeAnnotationsAttribute(ConstPool cp, int n, DataInputStream in)
+ throws IOException
+ {
+ super(cp, n, in);
+ }
+
+ /**
+ * Returns <code>num_annotations</code>.
+ */
+ public int numAnnotations() {
+ return ByteArray.readU16bit(info, 0);
+ }
+
+ /**
+ * Copies this attribute and returns a new copy.
+ */
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
+ Copier copier = new Copier(info, constPool, newCp, classnames);
+ try {
+ copier.annotationArray();
+ return new TypeAnnotationsAttribute(newCp, getName(), copier.close());
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @param oldname a JVM class name.
+ * @param newname a JVM class name.
+ */
+ @Override
+ void renameClass(String oldname, String newname) {
+ Map<String,String> map = new HashMap<String,String>();
+ map.put(oldname, newname);
+ renameClass(map);
+ }
+
+ @Override
+ void renameClass(Map<String,String> classnames) {
+ Renamer renamer = new Renamer(info, getConstPool(), classnames);
+ try {
+ renamer.annotationArray();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ void getRefClasses(Map<String,String> classnames) { renameClass(classnames); }
+
+ /**
+ * To visit each elements of the type annotation attribute,
+ * call {@code annotationArray()}.
+ *
+ * @see #annotationArray()
+ */
+ static class TAWalker extends AnnotationsAttribute.Walker {
+ SubWalker subWalker;
+
+ TAWalker(byte[] attrInfo) {
+ super(attrInfo);
+ subWalker = new SubWalker(attrInfo);
+ }
+
+ @Override
+ int annotationArray(int pos, int num) throws Exception {
+ for (int i = 0; i < num; i++) {
+ int targetType = info[pos] & 0xff;
+ pos = subWalker.targetInfo(pos + 1, targetType);
+ pos = subWalker.typePath(pos);
+ pos = annotation(pos);
+ }
+
+ return pos;
+ }
+ }
+
+ static class SubWalker {
+ byte[] info;
+
+ SubWalker(byte[] attrInfo) {
+ info = attrInfo;
+ }
+
+ final int targetInfo(int pos, int type) throws Exception {
+ switch (type) {
+ case 0x00:
+ case 0x01: {
+ int index = info[pos] & 0xff;
+ typeParameterTarget(pos, type, index);
+ return pos + 1; }
+ case 0x10: {
+ int index = ByteArray.readU16bit(info, pos);
+ supertypeTarget(pos, index);
+ return pos + 2; }
+ case 0x11:
+ case 0x12: {
+ int param = info[pos] & 0xff;
+ int bound = info[pos + 1] & 0xff;
+ typeParameterBoundTarget(pos, type, param, bound);
+ return pos + 2; }
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ emptyTarget(pos, type);
+ return pos;
+ case 0x16: {
+ int index = info[pos] & 0xff;
+ formalParameterTarget(pos, index);
+ return pos + 1; }
+ case 0x17: {
+ int index = ByteArray.readU16bit(info, pos);
+ throwsTarget(pos, index);
+ return pos + 2; }
+ case 0x40:
+ case 0x41: {
+ int len = ByteArray.readU16bit(info, pos);
+ return localvarTarget(pos + 2, type, len); }
+ case 0x42: {
+ int index = ByteArray.readU16bit(info, pos);
+ catchTarget(pos, index);
+ return pos + 2; }
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46: {
+ int offset = ByteArray.readU16bit(info, pos);
+ offsetTarget(pos, type, offset);
+ return pos + 2; }
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4a:
+ case 0x4b: {
+ int offset = ByteArray.readU16bit(info, pos);
+ int index = info[pos + 2] & 0xff;
+ typeArgumentTarget(pos, type, offset, index);
+ return pos + 3; }
+ default:
+ throw new RuntimeException("invalid target type: " + type);
+ }
+ }
+
+ void typeParameterTarget(int pos, int targetType, int typeParameterIndex)
+ throws Exception {}
+
+ void supertypeTarget(int pos, int superTypeIndex) throws Exception {}
+
+ void typeParameterBoundTarget(int pos, int targetType, int typeParameterIndex,
+ int boundIndex) throws Exception {}
+
+ void emptyTarget(int pos, int targetType) throws Exception {}
+
+ void formalParameterTarget(int pos, int formalParameterIndex) throws Exception {}
+
+ void throwsTarget(int pos, int throwsTypeIndex) throws Exception {}
+
+ int localvarTarget(int pos, int targetType, int tableLength) throws Exception {
+ for (int i = 0; i < tableLength; i++) {
+ int start = ByteArray.readU16bit(info, pos);
+ int length = ByteArray.readU16bit(info, pos + 2);
+ int index = ByteArray.readU16bit(info, pos + 4);
+ localvarTarget(pos, targetType, start, length, index);
+ pos += 6;
+ }
+
+ return pos;
+ }
+
+ void localvarTarget(int pos, int targetType, int startPc, int length, int index)
+ throws Exception {}
+
+ void catchTarget(int pos, int exceptionTableIndex) throws Exception {}
+
+ void offsetTarget(int pos, int targetType, int offset) throws Exception {}
+
+ void typeArgumentTarget(int pos, int targetType, int offset, int typeArgumentIndex)
+ throws Exception {}
+
+ final int typePath(int pos) throws Exception {
+ int len = info[pos++] & 0xff;
+ return typePath(pos, len);
+ }
+
+ int typePath(int pos, int pathLength) throws Exception {
+ for (int i = 0; i < pathLength; i++) {
+ int kind = info[pos] & 0xff;
+ int index = info[pos + 1] & 0xff;
+ typePath(pos, kind, index);
+ pos += 2;
+ }
+
+ return pos;
+ }
+
+ void typePath(int pos, int typePathKind, int typeArgumentIndex) throws Exception {}
+ }
+
+ static class Renamer extends AnnotationsAttribute.Renamer {
+ SubWalker sub;
+
+ Renamer(byte[] attrInfo, ConstPool cp, Map<String,String> map) {
+ super(attrInfo, cp, map);
+ sub = new SubWalker(attrInfo);
+ }
+
+ @Override
+ int annotationArray(int pos, int num) throws Exception {
+ for (int i = 0; i < num; i++) {
+ int targetType = info[pos] & 0xff;
+ pos = sub.targetInfo(pos + 1, targetType);
+ pos = sub.typePath(pos);
+ pos = annotation(pos);
+ }
+
+ return pos;
+ }
+ }
+
+ static class Copier extends AnnotationsAttribute.Copier {
+ SubCopier sub;
+
+ Copier(byte[] attrInfo, ConstPool src, ConstPool dest, Map<String,String> map) {
+ super(attrInfo, src, dest, map, false);
+ TypeAnnotationsWriter w = new TypeAnnotationsWriter(output, dest);
+ writer = w;
+ sub = new SubCopier(attrInfo, src, dest, map, w);
+ }
+
+ @Override
+ int annotationArray(int pos, int num) throws Exception {
+ writer.numAnnotations(num);
+ for (int i = 0; i < num; i++) {
+ int targetType = info[pos] & 0xff;
+ pos = sub.targetInfo(pos + 1, targetType);
+ pos = sub.typePath(pos);
+ pos = annotation(pos);
+ }
+
+ return pos;
+ }
+ }
+
+ static class SubCopier extends SubWalker {
+ ConstPool srcPool, destPool;
+ Map<String,String> classnames;
+ TypeAnnotationsWriter writer;
+
+ SubCopier(byte[] attrInfo, ConstPool src, ConstPool dest,
+ Map<String,String> map, TypeAnnotationsWriter w)
+ {
+ super(attrInfo);
+ srcPool = src;
+ destPool = dest;
+ classnames = map;
+ writer = w;
+ }
+
+ @Override
+ void typeParameterTarget(int pos, int targetType, int typeParameterIndex)
+ throws Exception
+ {
+ writer.typeParameterTarget(targetType, typeParameterIndex);
+ }
+
+ @Override
+ void supertypeTarget(int pos, int superTypeIndex) throws Exception {
+ writer.supertypeTarget(superTypeIndex);
+ }
+
+ @Override
+ void typeParameterBoundTarget(int pos, int targetType, int typeParameterIndex,
+ int boundIndex)
+ throws Exception
+ {
+ writer.typeParameterBoundTarget(targetType, typeParameterIndex, boundIndex);
+ }
+
+ @Override
+ void emptyTarget(int pos, int targetType) throws Exception {
+ writer.emptyTarget(targetType);
+ }
+
+ @Override
+ void formalParameterTarget(int pos, int formalParameterIndex) throws Exception {
+ writer.formalParameterTarget(formalParameterIndex);
+ }
+
+ @Override
+ void throwsTarget(int pos, int throwsTypeIndex) throws Exception {
+ writer.throwsTarget(throwsTypeIndex);
+ }
+
+ @Override
+ int localvarTarget(int pos, int targetType, int tableLength) throws Exception {
+ writer.localVarTarget(targetType, tableLength);
+ return super.localvarTarget(pos, targetType, tableLength);
+ }
+
+ @Override
+ void localvarTarget(int pos, int targetType, int startPc, int length, int index)
+ throws Exception
+ {
+ writer.localVarTargetTable(startPc, length, index);
+ }
+
+ @Override
+ void catchTarget(int pos, int exceptionTableIndex) throws Exception {
+ writer.catchTarget(exceptionTableIndex);
+ }
+
+ @Override
+ void offsetTarget(int pos, int targetType, int offset) throws Exception {
+ writer.offsetTarget(targetType, offset);
+ }
+
+ @Override
+ void typeArgumentTarget(int pos, int targetType, int offset, int typeArgumentIndex)
+ throws Exception
+ {
+ writer.typeArgumentTarget(targetType, offset, typeArgumentIndex);
+ }
+
+ @Override
+ int typePath(int pos, int pathLength) throws Exception {
+ writer.typePath(pathLength);
+ return super.typePath(pos, pathLength);
+ }
+
+ @Override
+ void typePath(int pos, int typePathKind, int typeArgumentIndex) throws Exception {
+ writer.typePathPath(typePathKind, typeArgumentIndex);
+ }
+ }
+}
diff --git a/src/main/javassist/bytecode/analysis/Analyzer.java b/src/main/javassist/bytecode/analysis/Analyzer.java
index ea56599..5d1250d 100644
--- a/src/main/javassist/bytecode/analysis/Analyzer.java
+++ b/src/main/javassist/bytecode/analysis/Analyzer.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -14,8 +15,6 @@
*/
package javassist.bytecode.analysis;
-import java.util.Iterator;
-
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
@@ -42,7 +41,7 @@ import javassist.bytecode.Opcode;
* // Method to analyze
* public Object doSomething(int x) {
* Number n;
- * if (x < 5) {
+ * if (x &lt; 5) {
* n = new Double(0);
* } else {
* n = new Long(0);
@@ -58,13 +57,13 @@ import javassist.bytecode.Opcode;
* // 5: new #18; //class java/lang/Double
* // 8: dup
* // 9: dconst_0
- * // 10: invokespecial #44; //Method java/lang/Double."<init>":(D)V
+ * // 10: invokespecial #44; //Method java/lang/Double."&lt;init&gt;":(D)V
* // 13: astore_2
* // 14: goto 26
* // 17: new #16; //class java/lang/Long
* // 20: dup
* // 21: lconst_1
- * // 22: invokespecial #47; //Method java/lang/Long."<init>":(J)V
+ * // 22: invokespecial #47; //Method java/lang/Long."&lt;init&gt;":(J)V
* // 25: astore_2
* // 26: aload_2
* // 27: areturn
@@ -362,9 +361,7 @@ public class Analyzer implements Opcode {
if (subroutine == null)
throw new BadBytecode("Ret on no subroutine! [pos = " + pos + "]");
- Iterator callerIter = subroutine.callers().iterator();
- while (callerIter.hasNext()) {
- int caller = ((Integer) callerIter.next()).intValue();
+ for (int caller:subroutine.callers()) {
int returnLoc = getNext(iter, caller, pos);
boolean changed = false;
@@ -376,8 +373,7 @@ public class Analyzer implements Opcode {
changed = old.mergeStack(frame);
}
- for (Iterator i = subroutine.accessed().iterator(); i.hasNext(); ) {
- int index = ((Integer)i.next()).intValue();
+ for (int index:subroutine.accessed()) {
Type oldType = old.getLocal(index);
Type newType = frame.getLocal(index);
if (oldType != newType) {
diff --git a/src/main/javassist/bytecode/analysis/ControlFlow.java b/src/main/javassist/bytecode/analysis/ControlFlow.java
new file mode 100644
index 0000000..e47c00d
--- /dev/null
+++ b/src/main/javassist/bytecode/analysis/ControlFlow.java
@@ -0,0 +1,519 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+
+package javassist.bytecode.analysis;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.stackmap.BasicBlock;
+
+/**
+ * Represents the control flow graph of a given method.
+ *
+ * <p>To obtain the control flow graph, do the following:</p>
+ *
+ * <pre>CtMethod m = ...
+ * ControlFlow cf = new ControlFlow(m);
+ * Block[] blocks = cf.basicBlocks();
+ * </pre>
+ *
+ * <p><code>blocks</code> is an array of basic blocks in
+ * that method body.</p>
+ *
+ * @see javassist.CtMethod
+ * @see Block
+ * @see Frame
+ * @see Analyzer
+ * @author Shigeru Chiba
+ * @since 3.16
+ */
+public class ControlFlow {
+ private CtClass clazz;
+ private MethodInfo methodInfo;
+ private Block[] basicBlocks;
+ private Frame[] frames;
+
+ /**
+ * Constructs a control-flow analyzer for the given method.
+ */
+ public ControlFlow(CtMethod method) throws BadBytecode {
+ this(method.getDeclaringClass(), method.getMethodInfo2());
+ }
+
+ /**
+ * Constructs a control-flow analyzer.
+ */
+ public ControlFlow(CtClass ctclazz, MethodInfo minfo) throws BadBytecode {
+ clazz = ctclazz;
+ methodInfo = minfo;
+ frames = null;
+ basicBlocks = (Block[])new BasicBlock.Maker() {
+ @Override
+ protected BasicBlock makeBlock(int pos) {
+ return new Block(pos, methodInfo);
+ }
+ @Override
+ protected BasicBlock[] makeArray(int size) {
+ return new Block[size];
+ }
+ }.make(minfo);
+ if (basicBlocks == null)
+ basicBlocks = new Block[0];
+ int size = basicBlocks.length;
+ int[] counters = new int[size];
+ for (int i = 0; i < size; i++) {
+ Block b = basicBlocks[i];
+ b.index = i;
+ b.entrances = new Block[b.incomings()];
+ counters[i] = 0;
+ }
+
+ for (int i = 0; i < size; i++) {
+ Block b = basicBlocks[i];
+ for (int k = 0; k < b.exits(); k++) {
+ Block e = b.exit(k);
+ e.entrances[counters[e.index]++] = b;
+ }
+
+ ControlFlow.Catcher[] catchers = b.catchers();
+ for (int k = 0; k < catchers.length; k++) {
+ Block catchBlock = catchers[k].node;
+ catchBlock.entrances[counters[catchBlock.index]++] = b;
+ }
+ }
+ }
+
+ /**
+ * Returns all the basic blocks in the method body.
+ *
+ * @return an array of basic blocks, the array has length 0 if
+ * the method doesn't have code.
+ */
+ public Block[] basicBlocks() {
+ return basicBlocks;
+ }
+
+ /**
+ * Returns the types of the local variables and stack frame entries
+ * available at the given position. If the byte at the position is
+ * not the first byte of an instruction, then this method returns
+ * null.
+ *
+ * @param pos the position.
+ */
+ public Frame frameAt(int pos) throws BadBytecode {
+ if (frames == null)
+ frames = new Analyzer().analyze(clazz, methodInfo);
+
+ return frames[pos];
+ }
+
+ /**
+ * Constructs a dominator tree. This method returns an array of
+ * the tree nodes. The first element of the array is the root
+ * of the tree.
+ *
+ * <p> The order of the elements is the same as that
+ * of the elements in the <code>Block</code> array returned
+ * by the <code>basicBlocks</code>
+ * method. If a <code>Block</code> object is at the i-th position
+ * in the <code>Block</code> array, then
+ * the <code>Node</code> object referring to that
+ * <code>Block</code> object is at the i-th position in the
+ * array returned by this method.
+ * For every array element <code>node</code>, its index in the
+ * array is equivalent to <code>node.block().index()</code>.
+ *
+ * @return an array of the tree nodes, or null if the method doesn't have code.
+ * @see Node#block()
+ * @see Block#index()
+ */
+ public Node[] dominatorTree() {
+ int size = basicBlocks.length;
+ if (size == 0)
+ return null;
+
+ Node[] nodes = new Node[size];
+ boolean[] visited = new boolean[size];
+ int[] distance = new int[size];
+ for (int i = 0; i < size; i++) {
+ nodes[i] = new Node(basicBlocks[i]);
+ visited[i] = false;
+ }
+
+ Access access = new Access(nodes) {
+ @Override
+ BasicBlock[] exits(Node n) { return n.block.getExit(); }
+ @Override
+ BasicBlock[] entrances(Node n) { return n.block.entrances; }
+ };
+ nodes[0].makeDepth1stTree(null, visited, 0, distance, access);
+ do {
+ for (int i = 0; i < size; i++)
+ visited[i] = false;
+ } while (nodes[0].makeDominatorTree(visited, distance, access));
+ Node.setChildren(nodes);
+ return nodes;
+ }
+
+ /**
+ * Constructs a post dominator tree. This method returns an array of
+ * the tree nodes. Note that the tree has multiple roots.
+ * The parent of the root nodes is null.
+ *
+ * <p> The order of the elements is the same as that
+ * of the elements in the <code>Block</code> array returned
+ * by the <code>basicBlocks</code>
+ * method. If a <code>Block</code> object is at the i-th position
+ * in the <code>Block</code> array, then
+ * the <code>Node</code> object referring to that
+ * <code>Block</code> object is at the i-th position in the
+ * array returned by this method.
+ * For every array element <code>node</code>, its index in the
+ * array is equivalent to <code>node.block().index()</code>.
+ *
+ * @return an array of the tree nodes, or null if the method doesn't have code.
+ * @see Node#block()
+ * @see Block#index()
+ */
+ public Node[] postDominatorTree() {
+ int size = basicBlocks.length;
+ if (size == 0)
+ return null;
+
+ Node[] nodes = new Node[size];
+ boolean[] visited = new boolean[size];
+ int[] distance = new int[size];
+ for (int i = 0; i < size; i++) {
+ nodes[i] = new Node(basicBlocks[i]);
+ visited[i] = false;
+ }
+
+ Access access = new Access(nodes) {
+ @Override
+ BasicBlock[] exits(Node n) { return n.block.entrances; }
+ @Override
+ BasicBlock[] entrances(Node n) { return n.block.getExit(); }
+ };
+
+ int counter = 0;
+ for (int i = 0; i < size; i++)
+ if (nodes[i].block.exits() == 0)
+ counter = nodes[i].makeDepth1stTree(null, visited, counter, distance, access);
+
+ boolean changed;
+ do {
+ for (int i = 0; i < size; i++)
+ visited[i] = false;
+
+ changed = false;
+ for (int i = 0; i < size; i++)
+ if (nodes[i].block.exits() == 0)
+ if (nodes[i].makeDominatorTree(visited, distance, access))
+ changed = true;
+ } while (changed);
+
+ Node.setChildren(nodes);
+ return nodes;
+ }
+
+ /**
+ * Basic block.
+ * It is a sequence of contiguous instructions that do not contain
+ * jump/branch instructions except the last one.
+ * Since Java6 or later does not allow <code>JSR</code>,
+ * we deal with <code>JSR</code> as a non-branch instruction.
+ */
+ public static class Block extends BasicBlock {
+ /**
+ * A field that can be freely used for storing extra data.
+ * A client program of this control-flow analyzer can append
+ * an additional attribute to a <code>Block</code> object.
+ * The Javassist library never accesses this field.
+ */
+ public Object clientData = null;
+
+ int index;
+ MethodInfo method;
+ Block[] entrances;
+
+ Block(int pos, MethodInfo minfo) {
+ super(pos);
+ method = minfo;
+ }
+
+ @Override
+ protected void toString2(StringBuffer sbuf) {
+ super.toString2(sbuf);
+ sbuf.append(", incoming{");
+ for (int i = 0; i < entrances.length; i++)
+ sbuf.append(entrances[i].position).append(", ");
+
+ sbuf.append("}");
+ }
+
+ BasicBlock[] getExit() { return exit; }
+
+ /**
+ * Returns the position of this block in the array of
+ * basic blocks that the <code>basicBlocks</code> method
+ * returns.
+ *
+ * @see #basicBlocks()
+ */
+ public int index() { return index; }
+
+ /**
+ * Returns the position of the first instruction
+ * in this block.
+ */
+ public int position() { return position; }
+
+ /**
+ * Returns the length of this block.
+ */
+ public int length() { return length; }
+
+ /**
+ * Returns the number of the control paths entering this block.
+ */
+ public int incomings() { return incoming; }
+
+ /**
+ * Returns the block that the control may jump into this block from.
+ */
+ public Block incoming(int n) {
+ return entrances[n];
+ }
+
+ /**
+ * Return the number of the blocks that may be executed
+ * after this block.
+ */
+ public int exits() { return exit == null ? 0 : exit.length; }
+
+ /**
+ * Returns the n-th block that may be executed after this
+ * block.
+ *
+ * @param n an index in the array of exit blocks.
+ */
+ public Block exit(int n) { return (Block)exit[n]; }
+
+ /**
+ * Returns catch clauses that will catch an exception thrown
+ * in this block.
+ */
+ public Catcher[] catchers() {
+ List<Catcher> catchers = new ArrayList<Catcher>();
+ BasicBlock.Catch c = toCatch;
+ while (c != null) {
+ catchers.add(new Catcher(c));
+ c = c.next;
+ }
+
+ return catchers.toArray(new Catcher[catchers.size()]);
+ }
+ }
+
+ static abstract class Access {
+ Node[] all;
+ Access(Node[] nodes) { all = nodes; }
+ Node node(BasicBlock b) { return all[((Block)b).index]; }
+ abstract BasicBlock[] exits(Node n);
+ abstract BasicBlock[] entrances(Node n);
+ }
+
+ /**
+ * A node of (post) dominator trees.
+ */
+ public static class Node {
+ private Block block;
+ private Node parent;
+ private Node[] children;
+
+ Node(Block b) {
+ block = b;
+ parent = null;
+ }
+
+ /**
+ * Returns a <code>String</code> representation.
+ */
+ @Override
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("Node[pos=").append(block().position());
+ sbuf.append(", parent=");
+ sbuf.append(parent == null ? "*" : Integer.toString(parent.block().position()));
+ sbuf.append(", children{");
+ for (int i = 0; i < children.length; i++)
+ sbuf.append(children[i].block().position()).append(", ");
+
+ sbuf.append("}]");
+ return sbuf.toString();
+ }
+
+ /**
+ * Returns the basic block indicated by this node.
+ */
+ public Block block() { return block; }
+
+ /**
+ * Returns the parent of this node.
+ */
+ public Node parent() { return parent; }
+
+ /**
+ * Returns the number of the children of this node.
+ */
+ public int children() { return children.length; }
+
+ /**
+ * Returns the n-th child of this node.
+ *
+ * @param n an index in the array of children.
+ */
+ public Node child(int n) { return children[n]; }
+
+ /*
+ * After executing this method, distance[] represents the post order of the tree nodes.
+ * It also represents distances from the root; a bigger number represents a shorter
+ * distance. parent is set to its parent in the depth first spanning tree.
+ */
+ int makeDepth1stTree(Node caller, boolean[] visited, int counter, int[] distance, Access access) {
+ int index = block.index;
+ if (visited[index])
+ return counter;
+
+ visited[index] = true;
+ parent = caller;
+ BasicBlock[] exits = access.exits(this);
+ if (exits != null)
+ for (int i = 0; i < exits.length; i++) {
+ Node n = access.node(exits[i]);
+ counter = n.makeDepth1stTree(this, visited, counter, distance, access);
+ }
+
+ distance[index] = counter++;
+ return counter;
+ }
+
+ boolean makeDominatorTree(boolean[] visited, int[] distance, Access access) {
+ int index = block.index;
+ if (visited[index])
+ return false;
+
+ visited[index] = true;
+ boolean changed = false;
+ BasicBlock[] exits = access.exits(this);
+ if (exits != null)
+ for (int i = 0; i < exits.length; i++) {
+ Node n = access.node(exits[i]);
+ if (n.makeDominatorTree(visited, distance, access))
+ changed = true;
+ }
+
+ BasicBlock[] entrances = access.entrances(this);
+ if (entrances != null)
+ for (int i = 0; i < entrances.length; i++) {
+ if (parent != null) {
+ Node n = getAncestor(parent, access.node(entrances[i]), distance);
+ if (n != parent) {
+ parent = n;
+ changed = true;
+ }
+ }
+ }
+
+ return changed;
+ }
+
+ private static Node getAncestor(Node n1, Node n2, int[] distance) {
+ while (n1 != n2) {
+ if (distance[n1.block.index] < distance[n2.block.index])
+ n1 = n1.parent;
+ else
+ n2 = n2.parent;
+
+ if (n1 == null || n2 == null)
+ return null;
+ }
+
+ return n1;
+ }
+
+ private static void setChildren(Node[] all) {
+ int size = all.length;
+ int[] nchildren = new int[size];
+ for (int i = 0; i < size; i++)
+ nchildren[i] = 0;
+
+ for (int i = 0; i < size; i++) {
+ Node p = all[i].parent;
+ if (p != null)
+ nchildren[p.block.index]++;
+ }
+
+ for (int i = 0; i < size; i++)
+ all[i].children = new Node[nchildren[i]];
+
+ for (int i = 0; i < size; i++)
+ nchildren[i] = 0;
+
+ for (int i = 0; i < size; i++) {
+ Node n = all[i];
+ Node p = n.parent;
+ if (p != null)
+ p.children[nchildren[p.block.index]++] = n;
+ }
+ }
+ }
+
+ /**
+ * Represents a catch clause.
+ */
+ public static class Catcher {
+ private Block node;
+ private int typeIndex;
+
+ Catcher(BasicBlock.Catch c) {
+ node = (Block)c.body;
+ typeIndex = c.typeIndex;
+ }
+
+ /**
+ * Returns the first block of the catch clause.
+ */
+ public Block block() { return node; }
+
+ /**
+ * Returns the name of the exception type that
+ * this catch clause catches.
+ */
+ public String type() {
+ if (typeIndex == 0)
+ return "java.lang.Throwable";
+ else
+ return node.method.getConstPool().getClassInfo(typeIndex);
+ }
+ }
+}
diff --git a/src/main/javassist/bytecode/analysis/Executor.java b/src/main/javassist/bytecode/analysis/Executor.java
index 562cc5d..63c4c7c 100644
--- a/src/main/javassist/bytecode/analysis/Executor.java
+++ b/src/main/javassist/bytecode/analysis/Executor.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -572,8 +573,9 @@ public class Executor implements Opcode {
case INVOKEINTERFACE:
evalInvokeIntfMethod(opcode, iter.u16bitAt(pos + 1), frame);
break;
- case 186:
- throw new RuntimeException("Bad opcode 186");
+ case INVOKEDYNAMIC:
+ evalInvokeDynamic(opcode, iter.u16bitAt(pos + 1), frame);
+ break;
case NEW:
frame.push(resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1))));
break;
@@ -595,7 +597,7 @@ public class Executor implements Opcode {
break;
case CHECKCAST:
verifyAssignable(Type.OBJECT, simplePop(frame));
- frame.push(typeFromDesc(constPool.getClassInfo(iter.u16bitAt(pos + 1))));
+ frame.push(typeFromDesc(constPool.getClassInfoByDescriptor(iter.u16bitAt(pos + 1))));
break;
case INSTANCEOF:
verifyAssignable(Type.OBJECT, simplePop(frame));
@@ -747,6 +749,20 @@ public class Executor implements Opcode {
simplePush(zeroExtend(returnType), frame);
}
+ private void evalInvokeDynamic(int opcode, int index, Frame frame) throws BadBytecode {
+ String desc = constPool.getInvokeDynamicType(index);
+ Type[] types = paramTypesFromDesc(desc);
+ int i = types.length;
+
+ while (i > 0)
+ verifyAssignable(zeroExtend(types[--i]), simplePop(frame));
+
+ // simplePop(frame); // assume CosntPool#REF_invokeStatic
+
+ Type returnType = returnTypeFromDesc(desc);
+ if (returnType != Type.VOID)
+ simplePush(zeroExtend(returnType), frame);
+ }
private void evalLDC(int index, Frame frame) throws BadBytecode {
int tag = constPool.getTag(index);
diff --git a/src/main/javassist/bytecode/analysis/Frame.java b/src/main/javassist/bytecode/analysis/Frame.java
index cf646f4..28c62e3 100644
--- a/src/main/javassist/bytecode/analysis/Frame.java
+++ b/src/main/javassist/bytecode/analysis/Frame.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -228,6 +229,7 @@ public class Frame {
return changed;
}
+ @Override
public String toString() {
StringBuffer buffer = new StringBuffer();
diff --git a/src/main/javassist/bytecode/analysis/FramePrinter.java b/src/main/javassist/bytecode/analysis/FramePrinter.java
index fc99cd3..2936844 100644
--- a/src/main/javassist/bytecode/analysis/FramePrinter.java
+++ b/src/main/javassist/bytecode/analysis/FramePrinter.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/bytecode/analysis/IntQueue.java b/src/main/javassist/bytecode/analysis/IntQueue.java
index d50cddc..9dbffb4 100644
--- a/src/main/javassist/bytecode/analysis/IntQueue.java
+++ b/src/main/javassist/bytecode/analysis/IntQueue.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -53,4 +54,4 @@ class IntQueue {
return value;
}
-} \ No newline at end of file
+}
diff --git a/src/main/javassist/bytecode/analysis/MultiArrayType.java b/src/main/javassist/bytecode/analysis/MultiArrayType.java
index 750116c..e430b85 100644
--- a/src/main/javassist/bytecode/analysis/MultiArrayType.java
+++ b/src/main/javassist/bytecode/analysis/MultiArrayType.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -33,6 +34,7 @@ public class MultiArrayType extends Type {
this.dims = dims;
}
+ @Override
public CtClass getCtClass() {
CtClass clazz = component.getCtClass();
if (clazz == null)
@@ -51,30 +53,37 @@ public class MultiArrayType extends Type {
}
}
+ @Override
boolean popChanged() {
return component.popChanged();
}
+ @Override
public int getDimensions() {
return dims;
}
+ @Override
public Type getComponent() {
return dims == 1 ? (Type)component : new MultiArrayType(component, dims - 1);
}
+ @Override
public int getSize() {
return 1;
}
+ @Override
public boolean isArray() {
return true;
}
+ @Override
public boolean isAssignableFrom(Type type) {
throw new UnsupportedOperationException("Not implemented");
}
+ @Override
public boolean isReference() {
return true;
}
@@ -114,6 +123,13 @@ public class MultiArrayType extends Type {
return component.isAssignableTo(typeRoot);
}
+
+ @Override
+ public int hashCode() {
+ return component.hashCode() + dims;
+ }
+
+ @Override
public boolean equals(Object o) {
if (! (o instanceof MultiArrayType))
return false;
@@ -122,6 +138,7 @@ public class MultiArrayType extends Type {
return component.equals(multi.component) && dims == multi.dims;
}
+ @Override
public String toString() {
// follows the same detailed formating scheme as component
return arrayName(component.toString(), dims);
diff --git a/src/main/javassist/bytecode/analysis/MultiType.java b/src/main/javassist/bytecode/analysis/MultiType.java
index 3fb1488..a8f6fc6 100644
--- a/src/main/javassist/bytecode/analysis/MultiType.java
+++ b/src/main/javassist/bytecode/analysis/MultiType.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,25 +16,24 @@
package javassist.bytecode.analysis;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.Map;
import javassist.CtClass;
/**
- * MultiType represents an unresolved type. Whenever two <literal>Type</literal>
+ * MultiType represents an unresolved type. Whenever two {@code Type}
* instances are merged, if they share more than one super type (either an
- * interface or a superclass), then a <literal>MultiType</literal> is used to
- * represent the possible super types. The goal of a <literal>MultiType</literal>
+ * interface or a superclass), then a {@code MultiType} is used to
+ * represent the possible super types. The goal of a {@code MultiType}
* is to reduce the set of possible types down to a single resolved type. This
* is done by eliminating non-assignable types from the typeset when the
- * <literal>MultiType</literal> is passed as an argument to
+ * {@code MultiType} is passed as an argument to
* {@link Type#isAssignableFrom(Type)}, as well as removing non-intersecting
* types during a merge.
*
- * Note: Currently the <litera>MultiType</literal> instance is reused as much
+ * Note: Currently the {@code MultiType} instance is reused as much
* as possible so that updates are visible from all frames. In addition, all
- * <literal>MultiType</literal> merge paths are also updated. This is somewhat
+ * {@code MultiType} merge paths are also updated. This is somewhat
* hackish, but it appears to handle most scenarios.
*
* @author Jason T. Greene
@@ -46,17 +46,17 @@ import javassist.CtClass;
* changes, and somehow communicating assignment changes to the Analyzer
*/
public class MultiType extends Type {
- private Map interfaces;
+ private Map<String,CtClass> interfaces;
private Type resolved;
private Type potentialClass;
private MultiType mergeSource;
private boolean changed = false;
- public MultiType(Map interfaces) {
+ public MultiType(Map<String,CtClass> interfaces) {
this(interfaces, null);
}
- public MultiType(Map interfaces, Type potentialClass) {
+ public MultiType(Map<String,CtClass> interfaces, Type potentialClass) {
super(null);
this.interfaces = interfaces;
this.potentialClass = potentialClass;
@@ -66,6 +66,7 @@ public class MultiType extends Type {
* Gets the class that corresponds with this type. If this information
* is not yet known, java.lang.Object will be returned.
*/
+ @Override
public CtClass getCtClass() {
if (resolved != null)
return resolved.getCtClass();
@@ -76,6 +77,7 @@ public class MultiType extends Type {
/**
* Always returns null since this type is never used for an array.
*/
+ @Override
public Type getComponent() {
return null;
}
@@ -83,6 +85,7 @@ public class MultiType extends Type {
/**
* Always returns 1, since this type is a reference.
*/
+ @Override
public int getSize() {
return 1;
}
@@ -90,6 +93,7 @@ public class MultiType extends Type {
/**
* Always reutnrs false since this type is never used for an array
*/
+ @Override
public boolean isArray() {
return false;
}
@@ -97,12 +101,14 @@ public class MultiType extends Type {
/**
* Returns true if the internal state has changed.
*/
+ @Override
boolean popChanged() {
boolean changed = this.changed;
this.changed = false;
return changed;
}
+ @Override
public boolean isAssignableFrom(Type type) {
throw new UnsupportedOperationException("Not implemented");
}
@@ -117,11 +123,11 @@ public class MultiType extends Type {
if (potentialClass != null && !type.isAssignableFrom(potentialClass))
potentialClass = null;
- Map map = mergeMultiAndSingle(this, type);
+ Map<String,CtClass> map = mergeMultiAndSingle(this, type);
if (map.size() == 1 && potentialClass == null) {
// Update previous merge paths to the same resolved type
- resolved = Type.get((CtClass)map.values().iterator().next());
+ resolved = Type.get(map.values().iterator().next());
propogateResolved();
return true;
@@ -167,16 +173,15 @@ public class MultiType extends Type {
*
* @return true
*/
+ @Override
public boolean isReference() {
return true;
}
- private Map getAllMultiInterfaces(MultiType type) {
- Map map = new HashMap();
+ private Map<String,CtClass> getAllMultiInterfaces(MultiType type) {
+ Map<String,CtClass> map = new HashMap<String,CtClass>();
- Iterator iter = type.interfaces.values().iterator();
- while (iter.hasNext()) {
- CtClass intf = (CtClass)iter.next();
+ for (CtClass intf:type.interfaces.values()) {
map.put(intf.getName(), intf);
getAllInterfaces(intf, map);
}
@@ -185,16 +190,16 @@ public class MultiType extends Type {
}
- private Map mergeMultiInterfaces(MultiType type1, MultiType type2) {
- Map map1 = getAllMultiInterfaces(type1);
- Map map2 = getAllMultiInterfaces(type2);
+ private Map<String,CtClass> mergeMultiInterfaces(MultiType type1, MultiType type2) {
+ Map<String,CtClass> map1 = getAllMultiInterfaces(type1);
+ Map<String,CtClass> map2 = getAllMultiInterfaces(type2);
return findCommonInterfaces(map1, map2);
}
- private Map mergeMultiAndSingle(MultiType multi, Type single) {
- Map map1 = getAllMultiInterfaces(multi);
- Map map2 = getAllInterfaces(single.getCtClass(), null);
+ private Map<String,CtClass> mergeMultiAndSingle(MultiType multi, Type single) {
+ Map<String,CtClass> map1 = getAllMultiInterfaces(multi);
+ Map<String,CtClass> map2 = getAllInterfaces(single.getCtClass(), null);
return findCommonInterfaces(map1, map2);
}
@@ -210,6 +215,7 @@ public class MultiType extends Type {
return false;
}
+ @Override
public Type merge(Type type) {
if (this == type)
return this;
@@ -234,7 +240,7 @@ public class MultiType extends Type {
}
}
- Map merged;
+ Map<String,CtClass> merged;
if (type instanceof MultiType) {
MultiType multi = (MultiType)type;
@@ -253,14 +259,13 @@ public class MultiType extends Type {
// Keep all previous merge paths up to date
if (merged.size() > 1 || (merged.size() == 1 && potentialClass != null)) {
// Check for changes
- if (merged.size() != interfaces.size()) {
+ if (merged.size() != interfaces.size())
changed = true;
- } else if (changed == false){
- Iterator iter = merged.keySet().iterator();
- while (iter.hasNext())
- if (! interfaces.containsKey(iter.next()))
+ else if (changed == false)
+ for (String key:merged.keySet())
+ if (!interfaces.containsKey(key))
changed = true;
- }
+
interfaces = merged;
propogateState();
@@ -268,19 +273,27 @@ public class MultiType extends Type {
return this;
}
- if (merged.size() == 1) {
- resolved = Type.get((CtClass) merged.values().iterator().next());
- } else if (potentialClass != null){
+ if (merged.size() == 1)
+ resolved = Type.get(merged.values().iterator().next());
+ else if (potentialClass != null)
resolved = potentialClass;
- } else {
+ else
resolved = OBJECT;
- }
propogateResolved();
return resolved;
}
+ @Override
+ public int hashCode() {
+ if (resolved != null)
+ return resolved.hashCode();
+
+ return interfaces.keySet().hashCode();
+ }
+
+ @Override
public boolean equals(Object o) {
if (! (o instanceof MultiType))
return false;
@@ -294,19 +307,18 @@ public class MultiType extends Type {
return interfaces.keySet().equals(multi.interfaces.keySet());
}
+ @Override
public String toString() {
if (resolved != null)
return resolved.toString();
StringBuffer buffer = new StringBuffer("{");
- Iterator iter = interfaces.keySet().iterator();
- while (iter.hasNext()) {
- buffer.append(iter.next());
- buffer.append(", ");
- }
- buffer.setLength(buffer.length() - 2);
+ for (String key:interfaces.keySet())
+ buffer.append(key).append(", ");
if (potentialClass != null)
- buffer.append(", *").append(potentialClass.toString());
+ buffer.append("*").append(potentialClass.toString());
+ else
+ buffer.setLength(buffer.length() - 2);
buffer.append("}");
return buffer.toString();
}
diff --git a/src/main/javassist/bytecode/analysis/Subroutine.java b/src/main/javassist/bytecode/analysis/Subroutine.java
index 1347813..dff3084 100644
--- a/src/main/javassist/bytecode/analysis/Subroutine.java
+++ b/src/main/javassist/bytecode/analysis/Subroutine.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -27,17 +28,17 @@ import java.util.Set;
*/
public class Subroutine {
//private Set callers = new HashSet();
- private List callers = new ArrayList();
- private Set access = new HashSet();
+ private List<Integer> callers = new ArrayList<Integer>();
+ private Set<Integer> access = new HashSet<Integer>();
private int start;
public Subroutine(int start, int caller) {
this.start = start;
- callers.add(new Integer(caller));
+ callers.add(caller);
}
public void addCaller(int caller) {
- callers.add(new Integer(caller));
+ callers.add(caller);
}
public int start() {
@@ -45,22 +46,23 @@ public class Subroutine {
}
public void access(int index) {
- access.add(new Integer(index));
+ access.add(index);
}
public boolean isAccessed(int index) {
- return access.contains(new Integer(index));
+ return access.contains(index);
}
- public Collection accessed() {
+ public Collection<Integer> accessed() {
return access;
}
- public Collection callers() {
+ public Collection<Integer> callers() {
return callers;
}
+ @Override
public String toString() {
return "start = " + start + " callers = " + callers.toString();
}
-} \ No newline at end of file
+}
diff --git a/src/main/javassist/bytecode/analysis/SubroutineScanner.java b/src/main/javassist/bytecode/analysis/SubroutineScanner.java
index f42202e..f981626 100644
--- a/src/main/javassist/bytecode/analysis/SubroutineScanner.java
+++ b/src/main/javassist/bytecode/analysis/SubroutineScanner.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -34,8 +35,8 @@ import javassist.bytecode.Opcode;
public class SubroutineScanner implements Opcode {
private Subroutine[] subroutines;
- Map subTable = new HashMap();
- Set done = new HashSet();
+ Map<Integer,Subroutine> subTable = new HashMap<Integer,Subroutine>();
+ Set<Integer> done = new HashSet<Integer>();
public Subroutine[] scan(MethodInfo method) throws BadBytecode {
@@ -61,10 +62,10 @@ public class SubroutineScanner implements Opcode {
private void scan(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode {
// Skip already processed blocks
- if (done.contains(new Integer(pos)))
+ if (done.contains(pos))
return;
- done.add(new Integer(pos));
+ done.add(pos);
int old = iter.lookAhead();
iter.move(pos);
@@ -102,10 +103,10 @@ public class SubroutineScanner implements Opcode {
if (Util.isJumpInstruction(opcode)) {
int target = Util.getJumpTarget(pos, iter);
if (opcode == JSR || opcode == JSR_W) {
- Subroutine s = (Subroutine) subTable.get(new Integer(target));
+ Subroutine s = subTable.get(target);
if (s == null) {
s = new Subroutine(target, pos);
- subTable.put(new Integer(target), s);
+ subTable.put(target, s);
scan(target, iter, s);
} else {
s.addCaller(pos);
diff --git a/src/main/javassist/bytecode/analysis/Type.java b/src/main/javassist/bytecode/analysis/Type.java
index 234f050..db02df3 100644
--- a/src/main/javassist/bytecode/analysis/Type.java
+++ b/src/main/javassist/bytecode/analysis/Type.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -14,10 +15,8 @@
*/
package javassist.bytecode.analysis;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
-import java.util.Iterator;
import java.util.Map;
import javassist.ClassPool;
@@ -43,7 +42,7 @@ public class Type {
private final CtClass clazz;
private final boolean special;
- private static final Map prims = new IdentityHashMap();
+ private static final Map<CtClass,Type> prims = new IdentityHashMap<CtClass,Type>();
/** Represents the double primitive type */
public static final Type DOUBLE = new Type(CtClass.doubleType);
/** Represents the boolean primitive type */
@@ -450,7 +449,7 @@ public class Type {
// If its Object, then try and find a common interface(s)
if (superClass.getSuperclass() == null) {
- Map interfaces = findCommonInterfaces(type);
+ Map<String,CtClass> interfaces = findCommonInterfaces(type);
if (interfaces.size() == 1)
return new Type((CtClass) interfaces.values().iterator().next());
if (interfaces.size() > 1)
@@ -461,7 +460,7 @@ public class Type {
}
// Check for a common interface that is not on the found supertype
- Map commonDeclared = findExclusiveDeclaredInterfaces(type, superClass);
+ Map<String,CtClass> commonDeclared = findExclusiveDeclaredInterfaces(type, superClass);
if (commonDeclared.size() > 0) {
return new MultiType(commonDeclared, new Type(superClass));
}
@@ -469,21 +468,19 @@ public class Type {
return new Type(superClass);
}
- private Map findCommonInterfaces(Type type) {
- Map typeMap = getAllInterfaces(type.clazz, null);
- Map thisMap = getAllInterfaces(this.clazz, null);
+ private Map<String,CtClass> findCommonInterfaces(Type type) {
+ Map<String,CtClass> typeMap = getAllInterfaces(type.clazz, null);
+ Map<String,CtClass> thisMap = getAllInterfaces(this.clazz, null);
return findCommonInterfaces(typeMap, thisMap);
}
- private Map findExclusiveDeclaredInterfaces(Type type, CtClass exclude) {
- Map typeMap = getDeclaredInterfaces(type.clazz, null);
- Map thisMap = getDeclaredInterfaces(this.clazz, null);
- Map excludeMap = getAllInterfaces(exclude, null);
+ private Map<String,CtClass> findExclusiveDeclaredInterfaces(Type type, CtClass exclude) {
+ Map<String,CtClass> typeMap = getDeclaredInterfaces(type.clazz, null);
+ Map<String,CtClass> thisMap = getDeclaredInterfaces(this.clazz, null);
+ Map<String,CtClass> excludeMap = getAllInterfaces(exclude, null);
- Iterator i = excludeMap.keySet().iterator();
- while (i.hasNext()) {
- Object intf = i.next();
+ for (String intf:excludeMap.keySet()) {
typeMap.remove(intf);
thisMap.remove(intf);
}
@@ -492,19 +489,21 @@ public class Type {
}
- Map findCommonInterfaces(Map typeMap, Map alterMap) {
- Iterator i = alterMap.keySet().iterator();
- while (i.hasNext()) {
- if (! typeMap.containsKey(i.next()))
- i.remove();
- }
+ Map<String,CtClass> findCommonInterfaces(Map<String,CtClass> typeMap, Map<String,CtClass> alterMap) {
+ if (alterMap == null)
+ alterMap = new HashMap<String,CtClass>();
+
+ if (typeMap == null||typeMap.isEmpty())
+ alterMap.clear();
+
+ for (String name:alterMap.keySet())
+ if (!typeMap.containsKey(name))
+ alterMap.remove(name);
// Reduce to subinterfaces
// This does not need to be recursive since we make a copy,
// and that copy contains all super types for the whole hierarchy
- i = new ArrayList(alterMap.values()).iterator();
- while (i.hasNext()) {
- CtClass intf = (CtClass) i.next();
+ for (CtClass intf:alterMap.values()) {
CtClass[] interfaces;
try {
interfaces = intf.getInterfaces();
@@ -512,24 +511,23 @@ public class Type {
throw new RuntimeException(e);
}
- for (int c = 0; c < interfaces.length; c++)
- alterMap.remove(interfaces[c].getName());
+ for (CtClass c:interfaces)
+ alterMap.remove(c.getName());
}
return alterMap;
}
- Map getAllInterfaces(CtClass clazz, Map map) {
+ Map<String,CtClass> getAllInterfaces(CtClass clazz, Map<String,CtClass> map) {
if (map == null)
- map = new HashMap();
+ map = new HashMap<String,CtClass>();
if (clazz.isInterface())
map.put(clazz.getName(), clazz);
do {
try {
CtClass[] interfaces = clazz.getInterfaces();
- for (int i = 0; i < interfaces.length; i++) {
- CtClass intf = interfaces[i];
+ for (CtClass intf:interfaces) {
map.put(intf.getName(), intf);
getAllInterfaces(intf, map);
}
@@ -543,9 +541,9 @@ public class Type {
return map;
}
- Map getDeclaredInterfaces(CtClass clazz, Map map) {
+ Map<String,CtClass> getDeclaredInterfaces(CtClass clazz, Map<String,CtClass> map) {
if (map == null)
- map = new HashMap();
+ map = new HashMap<String,CtClass>();
if (clazz.isInterface())
map.put(clazz.getName(), clazz);
@@ -557,8 +555,7 @@ public class Type {
throw new RuntimeException(e);
}
- for (int i = 0; i < interfaces.length; i++) {
- CtClass intf = interfaces[i];
+ for (CtClass intf:interfaces) {
map.put(intf.getName(), intf);
getDeclaredInterfaces(intf, map);
}
@@ -566,6 +563,12 @@ public class Type {
return map;
}
+ @Override
+ public int hashCode() {
+ return getClass().hashCode() + clazz.hashCode();
+ }
+
+ @Override
public boolean equals(Object o) {
if (! (o instanceof Type))
return false;
@@ -577,6 +580,7 @@ public class Type {
return one == two || (one != null && two != null && one.getName().equals(two.getName()));
}
+ @Override
public String toString() {
if (this == BOGUS)
return "BOGUS";
diff --git a/src/main/javassist/bytecode/analysis/Util.java b/src/main/javassist/bytecode/analysis/Util.java
index a8cdfcc..d2940f8 100644
--- a/src/main/javassist/bytecode/analysis/Util.java
+++ b/src/main/javassist/bytecode/analysis/Util.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/bytecode/analysis/package.html b/src/main/javassist/bytecode/analysis/package.html
index b141670..0279618 100644
--- a/src/main/javassist/bytecode/analysis/package.html
+++ b/src/main/javassist/bytecode/analysis/package.html
@@ -6,14 +6,15 @@ Bytecode Analysis API.
This allows the user to determine the type state of the stack and local variable table
at the start of every instruction. In addition this API can be used to validate
bytecode, find dead bytecode, and identify unnecessary checkcasts.
+Look at <code>ControlFlow</code> class first for details.
<p>The users of this package must know the specifications of
class file and Java bytecode. For more details, read this book:
-<ul>Tim Lindholm and Frank Yellin,
+<ul><li>Tim Lindholm and Frank Yellin,
"The Java Virtual Machine Specification 2nd Ed.",
Addison-Wesley, 1999.
-</ul>
+</li></ul>
</body>
</html>
diff --git a/src/main/javassist/bytecode/annotation/Annotation.java b/src/main/javassist/bytecode/annotation/Annotation.java
index b48cc8e..1cec468 100644
--- a/src/main/javassist/bytecode/annotation/Annotation.java
+++ b/src/main/javassist/bytecode/annotation/Annotation.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,17 +16,17 @@
package javassist.bytecode.annotation;
-import javassist.bytecode.ConstPool;
-import javassist.bytecode.Descriptor;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
-
-import java.io.IOException;
-import java.util.LinkedHashMap;
-import java.util.Set;
-import java.util.Iterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
/**
* The <code>annotation</code> structure.
@@ -52,7 +53,7 @@ public class Annotation {
ConstPool pool;
int typeIndex;
- LinkedHashMap members; // this sould be LinkedHashMap
+ Map<String,Pair> members; // this sould be LinkedHashMap
// but it is not supported by JDK 1.3.
/**
@@ -77,7 +78,7 @@ public class Annotation {
* Constructs an annotation including no members. A member can be
* later added to the created annotation by <code>addMemberValue()</code>.
*
- * @param typeName the name of the annotation interface type.
+ * @param typeName the fully-qualified name of the annotation interface type.
* @param cp the constant pool table.
*
* @see #addMemberValue(String, MemberValue)
@@ -105,17 +106,13 @@ public class Annotation {
throw new RuntimeException(
"Only interfaces are allowed for Annotation creation.");
- CtMethod methods[] = clazz.getDeclaredMethods();
- if (methods.length > 0) {
- members = new LinkedHashMap();
- }
+ CtMethod[] methods = clazz.getDeclaredMethods();
+ if (methods.length > 0)
+ members = new LinkedHashMap<String,Pair>();
- for (int i = 0; i < methods.length; i++) {
- CtClass returnType = methods[i].getReturnType();
- addMemberValue(methods[i].getName(),
- createMemberValue(cp, returnType));
-
- }
+ for (CtMethod m:methods)
+ addMemberValue(m.getName(),
+ createMemberValue(cp, m.getReturnType()));
}
/**
@@ -195,7 +192,7 @@ public class Annotation {
p.name = pool.addUtf8Info(name);
p.value = value;
if (members == null)
- members = new LinkedHashMap();
+ members = new LinkedHashMap<String,Pair>();
members.put(name, p);
}
@@ -203,7 +200,7 @@ public class Annotation {
private void addMemberValue(Pair pair) {
String name = pool.getUtf8Info(pair.name);
if (members == null)
- members = new LinkedHashMap();
+ members = new LinkedHashMap<String,Pair>();
members.put(name, pair);
}
@@ -211,18 +208,18 @@ public class Annotation {
/**
* Returns a string representation of the annotation.
*/
+ @Override
public String toString() {
StringBuffer buf = new StringBuffer("@");
buf.append(getTypeName());
if (members != null) {
buf.append("(");
- Iterator mit = members.keySet().iterator();
- while (mit.hasNext()) {
- String name = (String)mit.next();
- buf.append(name).append("=").append(getMemberValue(name));
- if (mit.hasNext())
- buf.append(", ");
+ for (String name:members.keySet()) {
+ buf.append(name).append("=")
+ .append(getMemberValue(name))
+ .append(", ");
}
+ buf.setLength(buf.length()-2);
buf.append(")");
}
@@ -243,11 +240,10 @@ public class Annotation {
*
* @return null if no members are defined.
*/
- public Set getMemberNames() {
+ public Set<String> getMemberNames() {
if (members == null)
return null;
- else
- return members.keySet();
+ return members.keySet();
}
/**
@@ -266,15 +262,9 @@ public class Annotation {
* @see javassist.bytecode.AnnotationDefaultAttribute
*/
public MemberValue getMemberValue(String name) {
- if (members == null)
+ if (members == null||members.get(name) == null)
return null;
- else {
- Pair p = (Pair)members.get(name);
- if (p == null)
- return null;
- else
- return p.value;
- }
+ return members.get(name).value;
}
/**
@@ -291,9 +281,21 @@ public class Annotation {
public Object toAnnotationType(ClassLoader cl, ClassPool cp)
throws ClassNotFoundException, NoSuchClassError
{
- return AnnotationImpl.make(cl,
- MemberValue.loadClass(cl, getTypeName()),
- cp, this);
+ Class<?> clazz = MemberValue.loadClass(cl, getTypeName());
+ try {
+ return AnnotationImpl.make(cl, clazz, cp, this);
+ }
+ catch (IllegalArgumentException e) {
+ /* AnnotationImpl.make() may throw this exception
+ * when it fails to make a proxy object for some
+ * reason.
+ */
+ throw new ClassNotFoundException(clazz.getName(), e);
+ }
+ catch (IllegalAccessError e2) {
+ // also IllegalAccessError
+ throw new ClassNotFoundException(clazz.getName(), e2);
+ }
}
/**
@@ -310,30 +312,35 @@ public class Annotation {
}
writer.annotation(typeName, members.size());
- Iterator it = members.values().iterator();
- while (it.hasNext()) {
- Pair pair = (Pair)it.next();
+ for (Pair pair:members.values()) {
writer.memberValuePair(pair.name);
pair.value.write(writer);
}
}
+ @Override
+ public int hashCode() {
+ return getTypeName().hashCode() +
+ (members == null ? 0 : members.hashCode());
+ }
+
/**
* Returns true if the given object represents the same annotation
* as this object. The equality test checks the member values.
*/
+ @Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null || obj instanceof Annotation == false)
return false;
-
+
Annotation other = (Annotation) obj;
if (getTypeName().equals(other.getTypeName()) == false)
return false;
- LinkedHashMap otherMembers = other.members;
+ Map<String,Pair> otherMembers = other.members;
if (members == otherMembers)
return true;
else if (members == null)
diff --git a/src/main/javassist/bytecode/annotation/AnnotationImpl.java b/src/main/javassist/bytecode/annotation/AnnotationImpl.java
index dfd23bb..e170609 100644
--- a/src/main/javassist/bytecode/annotation/AnnotationImpl.java
+++ b/src/main/javassist/bytecode/annotation/AnnotationImpl.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -37,24 +38,24 @@ import javassist.bytecode.MethodInfo;
public class AnnotationImpl implements InvocationHandler {
private static final String JDK_ANNOTATION_CLASS_NAME = "java.lang.annotation.Annotation";
private static Method JDK_ANNOTATION_TYPE_METHOD = null;
-
+
private Annotation annotation;
private ClassPool pool;
private ClassLoader classLoader;
- private transient Class annotationType;
+ private transient Class<?> annotationType;
private transient int cachedHashCode = Integer.MIN_VALUE;
static {
// Try to resolve the JDK annotation type method
try {
- Class clazz = Class.forName(JDK_ANNOTATION_CLASS_NAME);
+ Class<?> clazz = Class.forName(JDK_ANNOTATION_CLASS_NAME);
JDK_ANNOTATION_TYPE_METHOD = clazz.getMethod("annotationType", (Class[])null);
}
catch (Exception ignored) {
// Probably not JDK5+
}
}
-
+
/**
* Constructs an annotation object.
*
@@ -65,12 +66,14 @@ public class AnnotationImpl implements InvocationHandler {
* @param anon the annotation.
* @return the annotation
*/
- public static Object make(ClassLoader cl, Class clazz, ClassPool cp,
- Annotation anon) {
+ public static Object make(ClassLoader cl, Class<?> clazz, ClassPool cp,
+ Annotation anon)
+ throws IllegalArgumentException
+ {
AnnotationImpl handler = new AnnotationImpl(anon, cp, cl);
return Proxy.newProxyInstance(cl, new Class[] { clazz }, handler);
}
-
+
private AnnotationImpl(Annotation a, ClassPool cp, ClassLoader loader) {
annotation = a;
pool = cp;
@@ -92,7 +95,7 @@ public class AnnotationImpl implements InvocationHandler {
* @return the annotation class
* @throws NoClassDefFoundError when the class could not loaded
*/
- private Class getAnnotationType() {
+ private Class<?> getAnnotationType() {
if (annotationType == null) {
String typeName = annotation.getTypeName();
try {
@@ -106,7 +109,7 @@ public class AnnotationImpl implements InvocationHandler {
}
return annotationType;
}
-
+
/**
* Obtains the internal data structure representing the annotation.
*
@@ -123,6 +126,7 @@ public class AnnotationImpl implements InvocationHandler {
* <code>AnnotationImpl</code>. The <code>annotationType()</code> method
* is also available on the proxy instance.
*/
+ @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
@@ -130,12 +134,12 @@ public class AnnotationImpl implements InvocationHandler {
if (Object.class == method.getDeclaringClass()) {
if ("equals".equals(name)) {
Object obj = args[0];
- return new Boolean(checkEquals(obj));
+ return Boolean.valueOf(checkEquals(obj));
}
else if ("toString".equals(name))
return annotation.toString();
else if ("hashCode".equals(name))
- return new Integer(hashCode());
+ return Integer.valueOf(hashCode());
}
else if ("annotationType".equals(name)
&& method.getParameterTypes().length == 0)
@@ -144,10 +148,9 @@ public class AnnotationImpl implements InvocationHandler {
MemberValue mv = annotation.getMemberValue(name);
if (mv == null)
return getDefault(name, method);
- else
- return mv.getValue(classLoader, pool, method);
+ return mv.getValue(classLoader, pool, method);
}
-
+
private Object getDefault(String name, Method method)
throws ClassNotFoundException, RuntimeException
{
@@ -180,6 +183,7 @@ public class AnnotationImpl implements InvocationHandler {
/**
* Returns a hash code value for this object.
*/
+ @Override
public int hashCode() {
if (cachedHashCode == Integer.MIN_VALUE) {
int hashCode = 0;
@@ -214,15 +218,15 @@ public class AnnotationImpl implements InvocationHandler {
valueHashCode = arrayHashCode(value);
else
valueHashCode = value.hashCode();
- }
+ }
hashCode += 127 * name.hashCode() ^ valueHashCode;
}
-
+
cachedHashCode = hashCode;
}
return cachedHashCode;
}
-
+
/**
* Check that another annotation equals ourselves.
*
@@ -243,10 +247,10 @@ public class AnnotationImpl implements InvocationHandler {
}
}
- Class otherAnnotationType = (Class) JDK_ANNOTATION_TYPE_METHOD.invoke(obj, (Object[])null);
+ Class<?> otherAnnotationType = (Class<?>) JDK_ANNOTATION_TYPE_METHOD.invoke(obj);
if (getAnnotationType().equals(otherAnnotationType) == false)
return false;
-
+
Method[] methods = annotationType.getDeclaredMethods();
for (int i = 0; i < methods.length; ++ i) {
String name = methods[i].getName();
@@ -260,7 +264,7 @@ public class AnnotationImpl implements InvocationHandler {
value = mv.getValue(classLoader, pool, methods[i]);
if (value == null)
value = getDefault(name, methods[i]);
- otherValue = methods[i].invoke(obj, (Object[])null);
+ otherValue = methods[i].invoke(obj);
}
catch (RuntimeException e) {
throw e;
@@ -274,7 +278,7 @@ public class AnnotationImpl implements InvocationHandler {
if (value != null && value.equals(otherValue) == false)
return false;
}
-
+
return true;
}
@@ -291,7 +295,7 @@ public class AnnotationImpl implements InvocationHandler {
return 0;
int result = 1;
-
+
Object[] array = (Object[]) object;
for (int i = 0; i < array.length; ++i) {
int elementHashCode = 0;
diff --git a/src/main/javassist/bytecode/annotation/AnnotationMemberValue.java b/src/main/javassist/bytecode/annotation/AnnotationMemberValue.java
index 368c82a..7691e44 100644
--- a/src/main/javassist/bytecode/annotation/AnnotationMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/AnnotationMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -14,11 +15,12 @@
*/
package javassist.bytecode.annotation;
-import javassist.ClassPool;
-import javassist.bytecode.ConstPool;
import java.io.IOException;
import java.lang.reflect.Method;
+import javassist.ClassPool;
+import javassist.bytecode.ConstPool;
+
/**
* Nested annotation.
*
@@ -44,17 +46,18 @@ public class AnnotationMemberValue extends MemberValue {
value = a;
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m)
throws ClassNotFoundException
{
return AnnotationImpl.make(cl, getType(cl), cp, value);
}
- Class getType(ClassLoader cl) throws ClassNotFoundException {
+ @Override
+ Class<?> getType(ClassLoader cl) throws ClassNotFoundException {
if (value == null)
throw new ClassNotFoundException("no type specified");
- else
- return loadClass(cl, value.getTypeName());
+ return loadClass(cl, value.getTypeName());
}
/**
@@ -74,6 +77,7 @@ public class AnnotationMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
return value.toString();
}
@@ -81,6 +85,7 @@ public class AnnotationMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.annotationValue();
value.write(writer);
@@ -89,6 +94,7 @@ public class AnnotationMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitAnnotationMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/AnnotationsWriter.java b/src/main/javassist/bytecode/annotation/AnnotationsWriter.java
index f435d8f..8095fbe 100644
--- a/src/main/javassist/bytecode/annotation/AnnotationsWriter.java
+++ b/src/main/javassist/bytecode/annotation/AnnotationsWriter.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,7 +16,8 @@
package javassist.bytecode.annotation;
-import java.io.*;
+import java.io.IOException;
+import java.io.OutputStream;
import javassist.bytecode.ByteArray;
import javassist.bytecode.ConstPool;
@@ -27,16 +29,16 @@ import javassist.bytecode.ConstPool;
*
* <p>The following code snippet is an example of use of this class:
*
- * <ul><pre>
+ * <pre>
* ConstPool pool = ...;
* output = new ByteArrayOutputStream();
* writer = new AnnotationsWriter(output, pool);
*
* writer.numAnnotations(1);
* writer.annotation("Author", 2);
- * writer.memberValuePair("name");
+ * writer.memberValuePair("name"); // element_value_pair
* writer.constValueIndex("chiba");
- * writer.memberValuePair("address");
+ * writer.memberValuePair("address"); // element_value_pair
* writer.constValueIndex("tokyo");
*
* writer.close();
@@ -44,20 +46,20 @@ import javassist.bytecode.ConstPool;
* AnnotationsAttribute anno
* = new AnnotationsAttribute(pool, AnnotationsAttribute.visibleTag,
* attribute_info);
- * </pre></ul>
+ * </pre>
*
* <p>The code snippet above generates the annotation attribute
* corresponding to this annotation:
*
- * <ul><pre>
+ * <pre>
* &nbsp;@Author(name = "chiba", address = "tokyo")
- * </pre></ul>
+ * </pre>
*
* @see javassist.bytecode.AnnotationsAttribute
* @see javassist.bytecode.ParameterAnnotationsAttribute
*/
public class AnnotationsWriter {
- private OutputStream output;
+ protected OutputStream output;
private ConstPool pool;
/**
@@ -112,7 +114,7 @@ public class AnnotationsWriter {
* calls to <code>memberValuePair()</code>.
*
* @param type the annotation interface name.
- * @param numMemberValuePairs <code>num_member_value_pairs</code>
+ * @param numMemberValuePairs <code>num_element_value_pairs</code>
* in <code>annotation</code>.
*/
public void annotation(String type, int numMemberValuePairs)
@@ -127,7 +129,7 @@ public class AnnotationsWriter {
* calls to <code>memberValuePair()</code>.
*
* @param typeIndex <code>type_index</code> in <code>annotation</code>.
- * @param numMemberValuePairs <code>num_member_value_pairs</code>
+ * @param numMemberValuePairs <code>num_element_value_pairs</code>
* in <code>annotation</code>.
*/
public void annotation(int typeIndex, int numMemberValuePairs)
@@ -138,27 +140,27 @@ public class AnnotationsWriter {
}
/**
- * Writes an element of a <code>member_value_pairs</code> array
+ * Writes an element of a <code>element_value_pairs</code> array
* in <code>annotation</code>.
* This method must be followed by a
* call to <code>constValueIndex()</code>, <code>enumConstValue()</code>,
* etc.
*
- * @param memberName the name of the annotation type member.
+ * @param memberName the element name.
*/
public void memberValuePair(String memberName) throws IOException {
memberValuePair(pool.addUtf8Info(memberName));
}
/**
- * Writes an element of a <code>member_value_pairs</code> array
+ * Writes an element of a <code>element_value_pairs</code> array
* in <code>annotation</code>.
* This method must be followed by a
* call to <code>constValueIndex()</code>, <code>enumConstValue()</code>,
* etc.
*
- * @param memberNameIndex <code>member_name_index</code>
- * in <code>member_value_pairs</code> array.
+ * @param memberNameIndex <code>element_name_index</code>
+ * in <code>element_value_pairs</code> array.
*/
public void memberValuePair(int memberNameIndex) throws IOException {
write16bit(memberNameIndex);
@@ -166,7 +168,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>const_value_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param value the constant value.
*/
@@ -176,7 +178,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>const_value_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param value the constant value.
*/
@@ -186,7 +188,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>const_value_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param value the constant value.
*/
@@ -196,7 +198,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>const_value_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param value the constant value.
*/
@@ -206,7 +208,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>const_value_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param value the constant value.
*/
@@ -216,7 +218,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>const_value_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param value the constant value.
*/
@@ -226,7 +228,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>const_value_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param value the constant value.
*/
@@ -236,7 +238,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>const_value_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param value the constant value.
*/
@@ -246,7 +248,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>const_value_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param value the constant value.
*/
@@ -256,11 +258,11 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>const_value_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
- * @param tag <code>tag</code> in <code>member_value</code>.
+ * @param tag <code>tag</code> in <code>element_value</code>.
* @param index <code>const_value_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*/
public void constValueIndex(int tag, int index)
throws IOException
@@ -271,7 +273,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>enum_const_value</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param typeName the type name of the enum constant.
* @param constName the simple name of the enum constant.
@@ -285,12 +287,12 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>enum_const_value</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param typeNameIndex <code>type_name_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
* @param constNameIndex <code>const_name_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*/
public void enumConstValue(int typeNameIndex, int constNameIndex)
throws IOException
@@ -302,7 +304,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>class_info_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param name the class name.
*/
@@ -312,7 +314,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>class_info_index</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
*
* @param index <code>class_info_index</code>
*/
@@ -323,7 +325,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>annotation_value</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
* This method must be followed by a call to <code>annotation()</code>.
*/
public void annotationValue() throws IOException {
@@ -332,7 +334,7 @@ public class AnnotationsWriter {
/**
* Writes <code>tag</code> and <code>array_value</code>
- * in <code>member_value</code>.
+ * in <code>element_value</code>.
* This method must be followed by <code>numValues</code> calls
* to <code>constValueIndex()</code>, <code>enumConstValue()</code>,
* etc.
@@ -345,7 +347,7 @@ public class AnnotationsWriter {
write16bit(numValues);
}
- private void write16bit(int value) throws IOException {
+ protected void write16bit(int value) throws IOException {
byte[] buf = new byte[2];
ByteArray.write16bit(value, buf, 0);
output.write(buf);
diff --git a/src/main/javassist/bytecode/annotation/ArrayMemberValue.java b/src/main/javassist/bytecode/annotation/ArrayMemberValue.java
index 75ffe13..d1eeb27 100644
--- a/src/main/javassist/bytecode/annotation/ArrayMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/ArrayMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -14,12 +15,13 @@
*/
package javassist.bytecode.annotation;
-import javassist.ClassPool;
-import javassist.bytecode.ConstPool;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
+import javassist.ClassPool;
+import javassist.bytecode.ConstPool;
+
/**
* Array member.
*
@@ -50,6 +52,7 @@ public class ArrayMemberValue extends MemberValue {
values = null;
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method method)
throws ClassNotFoundException
{
@@ -58,7 +61,7 @@ public class ArrayMemberValue extends MemberValue {
"no array elements found: " + method.getName());
int size = values.length;
- Class clazz;
+ Class<?> clazz;
if (type == null) {
clazz = method.getReturnType().getComponentType();
if (clazz == null || size > 0)
@@ -75,7 +78,8 @@ public class ArrayMemberValue extends MemberValue {
return a;
}
- Class getType(ClassLoader cl) throws ClassNotFoundException {
+ @Override
+ Class<?> getType(ClassLoader cl) throws ClassNotFoundException {
if (type == null)
throw new ClassNotFoundException("no array type specified");
@@ -111,6 +115,7 @@ public class ArrayMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
StringBuffer buf = new StringBuffer("{");
if (values != null) {
@@ -128,8 +133,9 @@ public class ArrayMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
- int num = values.length;
+ int num = values == null ? 0 : values.length;
writer.arrayValue(num);
for (int i = 0; i < num; ++i)
values[i].write(writer);
@@ -138,6 +144,7 @@ public class ArrayMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitArrayMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/BooleanMemberValue.java b/src/main/javassist/bytecode/annotation/BooleanMemberValue.java
index 29432b1..c9ec3c3 100644
--- a/src/main/javassist/bytecode/annotation/BooleanMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/BooleanMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -14,11 +15,12 @@
*/
package javassist.bytecode.annotation;
-import javassist.ClassPool;
-import javassist.bytecode.ConstPool;
import java.io.IOException;
import java.lang.reflect.Method;
+import javassist.ClassPool;
+import javassist.bytecode.ConstPool;
+
/**
* Boolean constant value.
*
@@ -57,11 +59,13 @@ public class BooleanMemberValue extends MemberValue {
setValue(false);
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m) {
- return new Boolean(getValue());
+ return Boolean.valueOf(getValue());
}
- Class getType(ClassLoader cl) {
+ @Override
+ Class<?> getType(ClassLoader cl) {
return boolean.class;
}
@@ -82,6 +86,7 @@ public class BooleanMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
return getValue() ? "true" : "false";
}
@@ -89,6 +94,7 @@ public class BooleanMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.constValueIndex(getValue());
}
@@ -96,6 +102,7 @@ public class BooleanMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitBooleanMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/ByteMemberValue.java b/src/main/javassist/bytecode/annotation/ByteMemberValue.java
index 90763b0..be3f6e2 100644
--- a/src/main/javassist/bytecode/annotation/ByteMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/ByteMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -14,11 +15,12 @@
*/
package javassist.bytecode.annotation;
-import javassist.ClassPool;
-import javassist.bytecode.ConstPool;
import java.io.IOException;
import java.lang.reflect.Method;
+import javassist.ClassPool;
+import javassist.bytecode.ConstPool;
+
/**
* Byte constant value.
*
@@ -57,11 +59,13 @@ public class ByteMemberValue extends MemberValue {
setValue((byte)0);
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m) {
- return new Byte(getValue());
+ return Byte.valueOf(getValue());
}
- Class getType(ClassLoader cl) {
+ @Override
+ Class<?> getType(ClassLoader cl) {
return byte.class;
}
@@ -82,6 +86,7 @@ public class ByteMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
return Byte.toString(getValue());
}
@@ -89,6 +94,7 @@ public class ByteMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.constValueIndex(getValue());
}
@@ -96,6 +102,7 @@ public class ByteMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitByteMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/CharMemberValue.java b/src/main/javassist/bytecode/annotation/CharMemberValue.java
index f6691df..bf8d7b4 100644
--- a/src/main/javassist/bytecode/annotation/CharMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/CharMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,12 @@
package javassist.bytecode.annotation;
-import javassist.ClassPool;
-import javassist.bytecode.ConstPool;
import java.io.IOException;
import java.lang.reflect.Method;
+import javassist.ClassPool;
+import javassist.bytecode.ConstPool;
+
/**
* Char constant value.
*
@@ -58,11 +60,13 @@ public class CharMemberValue extends MemberValue {
setValue('\0');
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m) {
- return new Character(getValue());
+ return Character.valueOf(getValue());
}
- Class getType(ClassLoader cl) {
+ @Override
+ Class<?> getType(ClassLoader cl) {
return char.class;
}
@@ -83,6 +87,7 @@ public class CharMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
return Character.toString(getValue());
}
@@ -90,6 +95,7 @@ public class CharMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.constValueIndex(getValue());
}
@@ -97,6 +103,7 @@ public class CharMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitCharMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/ClassMemberValue.java b/src/main/javassist/bytecode/annotation/ClassMemberValue.java
index c29dbb2..e9fd7ec 100644
--- a/src/main/javassist/bytecode/annotation/ClassMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/ClassMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,14 @@
package javassist.bytecode.annotation;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
import javassist.ClassPool;
+import javassist.bytecode.BadBytecode;
import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
-import java.io.IOException;
-import java.lang.reflect.Method;
+import javassist.bytecode.SignatureAttribute;
/**
* Class value.
@@ -60,6 +64,7 @@ public class ClassMemberValue extends MemberValue {
setValue("java.lang.Class");
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m)
throws ClassNotFoundException {
final String classname = getValue();
@@ -85,7 +90,8 @@ public class ClassMemberValue extends MemberValue {
return loadClass(cl, classname);
}
- Class getType(ClassLoader cl) throws ClassNotFoundException {
+ @Override
+ Class<?> getType(ClassLoader cl) throws ClassNotFoundException {
return loadClass(cl, "java.lang.Class");
}
@@ -96,7 +102,11 @@ public class ClassMemberValue extends MemberValue {
*/
public String getValue() {
String v = cp.getUtf8Info(valueIndex);
- return Descriptor.toClassName(v);
+ try {
+ return SignatureAttribute.toTypeSignature(v).jvmTypeName();
+ } catch (BadBytecode e) {
+ throw new RuntimeException(e);
+ }
}
/**
@@ -112,13 +122,15 @@ public class ClassMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
- return "<" + getValue() + " class>";
+ return getValue().replace('$', '.') + ".class";
}
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.classInfoIndex(cp.getUtf8Info(valueIndex));
}
@@ -126,6 +138,7 @@ public class ClassMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitClassMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/DoubleMemberValue.java b/src/main/javassist/bytecode/annotation/DoubleMemberValue.java
index f2825ad..3d9fdb2 100644
--- a/src/main/javassist/bytecode/annotation/DoubleMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/DoubleMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,12 @@
package javassist.bytecode.annotation;
-import javassist.ClassPool;
-import javassist.bytecode.ConstPool;
import java.io.IOException;
import java.lang.reflect.Method;
+import javassist.ClassPool;
+import javassist.bytecode.ConstPool;
+
/**
* Double floating-point number constant value.
*
@@ -59,11 +61,13 @@ public class DoubleMemberValue extends MemberValue {
setValue(0.0);
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m) {
- return new Double(getValue());
+ return Double.valueOf(getValue());
}
- Class getType(ClassLoader cl) {
+ @Override
+ Class<?> getType(ClassLoader cl) {
return double.class;
}
@@ -84,6 +88,7 @@ public class DoubleMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
return Double.toString(getValue());
}
@@ -91,6 +96,7 @@ public class DoubleMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.constValueIndex(getValue());
}
@@ -98,6 +104,7 @@ public class DoubleMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitDoubleMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/EnumMemberValue.java b/src/main/javassist/bytecode/annotation/EnumMemberValue.java
index effec09..a0a4e03 100644
--- a/src/main/javassist/bytecode/annotation/EnumMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/EnumMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -55,6 +56,7 @@ public class EnumMemberValue extends MemberValue {
typeIndex = valueIndex = 0;
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m)
throws ClassNotFoundException
{
@@ -69,7 +71,8 @@ public class EnumMemberValue extends MemberValue {
}
}
- Class getType(ClassLoader cl) throws ClassNotFoundException {
+ @Override
+ Class<?> getType(ClassLoader cl) throws ClassNotFoundException {
return loadClass(cl, getType());
}
@@ -85,7 +88,7 @@ public class EnumMemberValue extends MemberValue {
/**
* Changes the enum type name.
*
- * @param typename a fully-qualified type name.
+ * @param typename a fully-qualified type name.
*/
public void setType(String typename) {
typeIndex = cp.addUtf8Info(Descriptor.of(typename));
@@ -105,6 +108,7 @@ public class EnumMemberValue extends MemberValue {
valueIndex = cp.addUtf8Info(name);
}
+ @Override
public String toString() {
return getType() + "." + getValue();
}
@@ -112,6 +116,7 @@ public class EnumMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.enumConstValue(cp.getUtf8Info(typeIndex), getValue());
}
@@ -119,6 +124,7 @@ public class EnumMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitEnumMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/FloatMemberValue.java b/src/main/javassist/bytecode/annotation/FloatMemberValue.java
index 7188b5e..ef99379 100644
--- a/src/main/javassist/bytecode/annotation/FloatMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/FloatMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,12 @@
package javassist.bytecode.annotation;
-import javassist.ClassPool;
-import javassist.bytecode.ConstPool;
import java.io.IOException;
import java.lang.reflect.Method;
+import javassist.ClassPool;
+import javassist.bytecode.ConstPool;
+
/**
* Floating-point number constant value.
*
@@ -59,11 +61,13 @@ public class FloatMemberValue extends MemberValue {
setValue(0.0F);
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m) {
- return new Float(getValue());
+ return Float.valueOf(getValue());
}
- Class getType(ClassLoader cl) {
+ @Override
+ Class<?> getType(ClassLoader cl) {
return float.class;
}
@@ -84,6 +88,7 @@ public class FloatMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
return Float.toString(getValue());
}
@@ -91,6 +96,7 @@ public class FloatMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.constValueIndex(getValue());
}
@@ -98,6 +104,7 @@ public class FloatMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitFloatMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/IntegerMemberValue.java b/src/main/javassist/bytecode/annotation/IntegerMemberValue.java
index 2f2a907..538b620 100644
--- a/src/main/javassist/bytecode/annotation/IntegerMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/IntegerMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,12 @@
package javassist.bytecode.annotation;
-import javassist.ClassPool;
-import javassist.bytecode.ConstPool;
import java.io.IOException;
import java.lang.reflect.Method;
+import javassist.ClassPool;
+import javassist.bytecode.ConstPool;
+
/**
* Integer constant value.
*
@@ -64,11 +66,13 @@ public class IntegerMemberValue extends MemberValue {
setValue(0);
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m) {
- return new Integer(getValue());
+ return Integer.valueOf(getValue());
}
- Class getType(ClassLoader cl) {
+ @Override
+ Class<?> getType(ClassLoader cl) {
return int.class;
}
@@ -89,6 +93,7 @@ public class IntegerMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
return Integer.toString(getValue());
}
@@ -96,6 +101,7 @@ public class IntegerMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.constValueIndex(getValue());
}
@@ -103,6 +109,7 @@ public class IntegerMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitIntegerMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/LongMemberValue.java b/src/main/javassist/bytecode/annotation/LongMemberValue.java
index 2afd4a0..3dd29a3 100644
--- a/src/main/javassist/bytecode/annotation/LongMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/LongMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,12 @@
package javassist.bytecode.annotation;
-import javassist.ClassPool;
-import javassist.bytecode.ConstPool;
import java.io.IOException;
import java.lang.reflect.Method;
+import javassist.ClassPool;
+import javassist.bytecode.ConstPool;
+
/**
* Long integer constant value.
*
@@ -58,11 +60,13 @@ public class LongMemberValue extends MemberValue {
setValue(0L);
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m) {
- return new Long(getValue());
+ return Long.valueOf(getValue());
}
- Class getType(ClassLoader cl) {
+ @Override
+ Class<?> getType(ClassLoader cl) {
return long.class;
}
@@ -83,6 +87,7 @@ public class LongMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
return Long.toString(getValue());
}
@@ -90,6 +95,7 @@ public class LongMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.constValueIndex(getValue());
}
@@ -97,6 +103,7 @@ public class LongMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitLongMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/MemberValue.java b/src/main/javassist/bytecode/annotation/MemberValue.java
index 18796ee..da99885 100644
--- a/src/main/javassist/bytecode/annotation/MemberValue.java
+++ b/src/main/javassist/bytecode/annotation/MemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,14 +16,13 @@
package javassist.bytecode.annotation;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
import javassist.ClassPool;
import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
-import java.io.IOException;
-import java.lang.reflect.Array;
-import java.lang.reflect.Method;
-
/**
* The value of a member declared in an annotation.
*
@@ -46,9 +46,9 @@ public abstract class MemberValue {
abstract Object getValue(ClassLoader cl, ClassPool cp, Method m)
throws ClassNotFoundException;
- abstract Class getType(ClassLoader cl) throws ClassNotFoundException;
+ abstract Class<?> getType(ClassLoader cl) throws ClassNotFoundException;
- static Class loadClass(ClassLoader cl, String classname)
+ static Class<?> loadClass(ClassLoader cl, String classname)
throws ClassNotFoundException, NoSuchClassError
{
try {
@@ -58,10 +58,10 @@ public abstract class MemberValue {
throw new NoSuchClassError(classname, e);
}
}
-
+
private static String convertFromArray(String classname)
{
- int index = classname.indexOf("[]");
+ int index = classname.indexOf("[]");
if (index != -1) {
String rawType = classname.substring(0, index);
StringBuffer sb = new StringBuffer(Descriptor.of(rawType));
diff --git a/src/main/javassist/bytecode/annotation/MemberValueVisitor.java b/src/main/javassist/bytecode/annotation/MemberValueVisitor.java
index 6944bd0..4982f84 100644
--- a/src/main/javassist/bytecode/annotation/MemberValueVisitor.java
+++ b/src/main/javassist/bytecode/annotation/MemberValueVisitor.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/bytecode/annotation/NoSuchClassError.java b/src/main/javassist/bytecode/annotation/NoSuchClassError.java
index c6d1a12..a2b23b3 100644
--- a/src/main/javassist/bytecode/annotation/NoSuchClassError.java
+++ b/src/main/javassist/bytecode/annotation/NoSuchClassError.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2009 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -20,6 +21,8 @@ package javassist.bytecode.annotation;
* It keeps the name of the class that caused this error.
*/
public class NoSuchClassError extends Error {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private String className;
/**
diff --git a/src/main/javassist/bytecode/annotation/ShortMemberValue.java b/src/main/javassist/bytecode/annotation/ShortMemberValue.java
index 3ccf380..277c282 100644
--- a/src/main/javassist/bytecode/annotation/ShortMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/ShortMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,12 @@
package javassist.bytecode.annotation;
-import javassist.ClassPool;
-import javassist.bytecode.ConstPool;
import java.io.IOException;
import java.lang.reflect.Method;
+import javassist.ClassPool;
+import javassist.bytecode.ConstPool;
+
/**
* Short integer constant value.
*
@@ -58,11 +60,13 @@ public class ShortMemberValue extends MemberValue {
setValue((short)0);
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m) {
- return new Short(getValue());
+ return Short.valueOf(getValue());
}
- Class getType(ClassLoader cl) {
+ @Override
+ Class<?> getType(ClassLoader cl) {
return short.class;
}
@@ -83,6 +87,7 @@ public class ShortMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
return Short.toString(getValue());
}
@@ -90,6 +95,7 @@ public class ShortMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.constValueIndex(getValue());
}
@@ -97,6 +103,7 @@ public class ShortMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitShortMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/StringMemberValue.java b/src/main/javassist/bytecode/annotation/StringMemberValue.java
index 970fb8f..48fe1f6 100644
--- a/src/main/javassist/bytecode/annotation/StringMemberValue.java
+++ b/src/main/javassist/bytecode/annotation/StringMemberValue.java
@@ -5,7 +5,8 @@
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,12 @@
package javassist.bytecode.annotation;
-import javassist.ClassPool;
-import javassist.bytecode.ConstPool;
import java.io.IOException;
import java.lang.reflect.Method;
+import javassist.ClassPool;
+import javassist.bytecode.ConstPool;
+
/**
* String constant value.
*
@@ -58,11 +60,13 @@ public class StringMemberValue extends MemberValue {
setValue("");
}
+ @Override
Object getValue(ClassLoader cl, ClassPool cp, Method m) {
return getValue();
}
- Class getType(ClassLoader cl) {
+ @Override
+ Class<?> getType(ClassLoader cl) {
return String.class;
}
@@ -83,6 +87,7 @@ public class StringMemberValue extends MemberValue {
/**
* Obtains the string representation of this object.
*/
+ @Override
public String toString() {
return "\"" + getValue() + "\"";
}
@@ -90,6 +95,7 @@ public class StringMemberValue extends MemberValue {
/**
* Writes the value.
*/
+ @Override
public void write(AnnotationsWriter writer) throws IOException {
writer.constValueIndex(getValue());
}
@@ -97,6 +103,7 @@ public class StringMemberValue extends MemberValue {
/**
* Accepts a visitor.
*/
+ @Override
public void accept(MemberValueVisitor visitor) {
visitor.visitStringMemberValue(this);
}
diff --git a/src/main/javassist/bytecode/annotation/TypeAnnotationsWriter.java b/src/main/javassist/bytecode/annotation/TypeAnnotationsWriter.java
new file mode 100644
index 0000000..6f765b4
--- /dev/null
+++ b/src/main/javassist/bytecode/annotation/TypeAnnotationsWriter.java
@@ -0,0 +1,175 @@
+package javassist.bytecode.annotation;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javassist.bytecode.ConstPool;
+
+/**
+ * A convenience class for constructing a
+ * {@code ..TypeAnnotations_attribute}.
+ * See the source code of the {@link javassist.bytecode.TypeAnnotationsAttribute} class.
+ *
+ * @since 3.19
+ */
+public class TypeAnnotationsWriter extends AnnotationsWriter {
+ /**
+ * Constructs with the given output stream.
+ *
+ * @param os the output stream.
+ * @param cp the constant pool.
+ */
+ public TypeAnnotationsWriter(OutputStream os, ConstPool cp) {
+ super(os, cp);
+ }
+
+ /**
+ * Writes {@code num_annotations} in
+ * {@code Runtime(In)VisibleTypeAnnotations_attribute}.
+ * It must be followed by {@code num} instances of {@code type_annotation}.
+ */
+ @Override
+ public void numAnnotations(int num) throws IOException {
+ super.numAnnotations(num);
+ }
+
+ /**
+ * Writes {@code target_type} and {@code type_parameter_target}
+ * of {@code target_info} union.
+ */
+ public void typeParameterTarget(int targetType, int typeParameterIndex)
+ throws IOException
+ {
+ output.write(targetType);
+ output.write(typeParameterIndex);
+ }
+
+ /**
+ * Writes {@code target_type} and {@code supertype_target}
+ * of {@code target_info} union.
+ */
+ public void supertypeTarget(int supertypeIndex)
+ throws IOException
+ {
+ output.write(0x10);
+ write16bit(supertypeIndex);
+ }
+
+ /**
+ * Writes {@code target_type} and {@code type_parameter_bound_target}
+ * of {@code target_info} union.
+ */
+ public void typeParameterBoundTarget(int targetType, int typeParameterIndex, int boundIndex)
+ throws IOException
+ {
+ output.write(targetType);
+ output.write(typeParameterIndex);
+ output.write(boundIndex);
+ }
+
+ /**
+ * Writes {@code target_type} and {@code empty_target}
+ * of {@code target_info} union.
+ */
+ public void emptyTarget(int targetType) throws IOException {
+ output.write(targetType);
+ }
+
+ /**
+ * Writes {@code target_type} and {@code type_parameter_target}
+ * of {@code target_info} union.
+ */
+ public void formalParameterTarget(int formalParameterIndex)
+ throws IOException
+ {
+ output.write(0x16);
+ output.write(formalParameterIndex);
+ }
+
+ /**
+ * Writes {@code target_type} and {@code throws_target}
+ * of {@code target_info} union.
+ */
+ public void throwsTarget(int throwsTypeIndex)
+ throws IOException
+ {
+ output.write(0x17);
+ write16bit(throwsTypeIndex);
+ }
+
+ /**
+ * Writes {@code target_type} and {@code localvar_target}
+ * of {@code target_info} union.
+ * It must be followed by {@code tableLength} calls
+ * to {@code localVarTargetTable}.
+ */
+ public void localVarTarget(int targetType, int tableLength)
+ throws IOException
+ {
+ output.write(targetType);
+ write16bit(tableLength);
+ }
+
+ /**
+ * Writes an element of {@code table[]} of {@code localvar_target}
+ * of {@code target_info} union.
+ */
+ public void localVarTargetTable(int startPc, int length, int index)
+ throws IOException
+ {
+ write16bit(startPc);
+ write16bit(length);
+ write16bit(index);
+ }
+
+ /**
+ * Writes {@code target_type} and {@code catch_target}
+ * of {@code target_info} union.
+ */
+ public void catchTarget(int exceptionTableIndex)
+ throws IOException
+ {
+ output.write(0x42);
+ write16bit(exceptionTableIndex);
+ }
+
+ /**
+ * Writes {@code target_type} and {@code offset_target}
+ * of {@code target_info} union.
+ */
+ public void offsetTarget(int targetType, int offset)
+ throws IOException
+ {
+ output.write(targetType);
+ write16bit(offset);
+ }
+
+ /**
+ * Writes {@code target_type} and {@code type_argument_target}
+ * of {@code target_info} union.
+ */
+ public void typeArgumentTarget(int targetType, int offset, int type_argument_index)
+ throws IOException
+ {
+ output.write(targetType);
+ write16bit(offset);
+ output.write(type_argument_index);
+ }
+
+ /**
+ * Writes {@code path_length} of {@code type_path}.
+ */
+ public void typePath(int pathLength) throws IOException {
+ output.write(pathLength);
+ }
+
+ /**
+ * Writes an element of {@code path[]} of {@code type_path}.
+ */
+ public void typePathPath(int typePathKind, int typeArgumentIndex)
+ throws IOException
+ {
+ output.write(typePathKind);
+ output.write(typeArgumentIndex);
+ }
+}
diff --git a/src/main/javassist/bytecode/package.html b/src/main/javassist/bytecode/package.html
index 9da3888..e084193 100644
--- a/src/main/javassist/bytecode/package.html
+++ b/src/main/javassist/bytecode/package.html
@@ -9,10 +9,10 @@ bytecode instruction, and so on.
<p>The users of this package must know the specifications of
class file and Java bytecode. For more details, read this book:
-<ul>Tim Lindholm and Frank Yellin,
+<ul><li>Tim Lindholm and Frank Yellin,
"The Java Virtual Machine Specification 2nd Ed.",
Addison-Wesley, 1999.
-</ul>
+</li></ul>
</body>
</html>
diff --git a/src/main/javassist/bytecode/stackmap/BasicBlock.java b/src/main/javassist/bytecode/stackmap/BasicBlock.java
index e5b64d3..231ffc3 100644
--- a/src/main/javassist/bytecode/stackmap/BasicBlock.java
+++ b/src/main/javassist/bytecode/stackmap/BasicBlock.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,22 +16,38 @@
package javassist.bytecode.stackmap;
-import javassist.bytecode.*;
-import java.util.HashMap;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ExceptionTable;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
/**
* A basic block is a sequence of bytecode that does not contain jump/branch
* instructions except at the last bytecode.
- * Since Java6 or later does not allow JSR, this class deals with JSR as a
- * non-branch instruction.
+ * Since Java7 or later does not allow JSR, this class throws an exception when
+ * it finds JSR.
*/
public class BasicBlock {
- public int position, length;
- public int incoming; // the number of incoming branches.
- public BasicBlock[] exit; // null if the block is a leaf.
- public boolean stop; // true if the block ends with an unconditional jump.
- public Catch toCatch;
+ static class JsrBytecode extends BadBytecode {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ JsrBytecode() { super("JSR"); }
+ }
+
+ protected int position, length;
+ protected int incoming; // the number of incoming branches.
+ protected BasicBlock[] exit; // null if the block is a leaf.
+ protected boolean stop; // true if the block ends with an unconditional jump.
+ protected Catch toCatch;
protected BasicBlock(int pos) {
position = pos;
@@ -41,19 +58,17 @@ public class BasicBlock {
public static BasicBlock find(BasicBlock[] blocks, int pos)
throws BadBytecode
{
- for (int i = 0; i < blocks.length; i++) {
- int iPos = blocks[i].position;
- if (iPos <= pos && pos < iPos + blocks[i].length)
- return blocks[i];
- }
+ for (BasicBlock b:blocks)
+ if (b.position <= pos && pos < b.position + b.length)
+ return b;
throw new BadBytecode("no basic block at " + pos);
}
public static class Catch {
- Catch next;
- BasicBlock body;
- int typeIndex;
+ public Catch next;
+ public BasicBlock body;
+ public int typeIndex;
Catch(BasicBlock b, int i, Catch c) {
body = b;
typeIndex = i;
@@ -61,6 +76,7 @@ public class BasicBlock {
}
}
+ @Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
String cname = this.getClass().getName();
@@ -76,10 +92,9 @@ public class BasicBlock {
sbuf.append("pos=").append(position).append(", len=")
.append(length).append(", in=").append(incoming)
.append(", exit{");
- if (exit != null) {
- for (int i = 0; i < exit.length; i++)
- sbuf.append(exit[i].position).append(", ");
- }
+ if (exit != null)
+ for (BasicBlock b:exit)
+ sbuf.append(b.position).append(",");
sbuf.append("}, {");
Catch th = toCatch;
@@ -92,11 +107,15 @@ public class BasicBlock {
sbuf.append("}");
}
- static class Mark implements Comparable {
+ /**
+ * A Mark indicates the position of a branch instruction
+ * or a branch target.
+ */
+ static class Mark implements Comparable<Mark> {
int position;
BasicBlock block;
BasicBlock[] jump;
- boolean alwaysJmp; // true if a unconditional branch.
+ boolean alwaysJmp; // true if an unconditional branch.
int size; // 0 unless the mark indicates RETURN etc.
Catch catcher;
@@ -109,13 +128,11 @@ public class BasicBlock {
catcher = null;
}
- public int compareTo(Object obj) {
- if (obj instanceof Mark) {
- int pos = ((Mark)obj).position;
- return position - pos;
- }
-
- return -1;
+ @Override
+ public int compareTo(Mark obj) {
+ if (null == obj)
+ return -1;
+ return position - obj.position;
}
void setJump(BasicBlock[] bb, int s, boolean always) {
@@ -163,7 +180,7 @@ public class BasicBlock {
ExceptionTable et)
throws BadBytecode
{
- HashMap marks = makeMarks(ci, begin, end, et);
+ Map<Integer,Mark> marks = makeMarks(ci, begin, end, et);
BasicBlock[] bb = makeBlocks(marks);
addCatchers(bb, et);
return bb;
@@ -171,24 +188,24 @@ public class BasicBlock {
/* Branch target
*/
- private Mark makeMark(HashMap table, int pos) {
+ private Mark makeMark(Map<Integer,Mark> table, int pos) {
return makeMark0(table, pos, true, true);
}
/* Branch instruction.
* size > 0
*/
- private Mark makeMark(HashMap table, int pos, BasicBlock[] jump,
+ private Mark makeMark(Map<Integer,Mark> table, int pos, BasicBlock[] jump,
int size, boolean always) {
Mark m = makeMark0(table, pos, false, false);
m.setJump(jump, size, always);
return m;
}
- private Mark makeMark0(HashMap table, int pos,
+ private Mark makeMark0(Map<Integer,Mark> table, int pos,
boolean isBlockBegin, boolean isTarget) {
- Integer p = new Integer(pos);
- Mark m = (Mark)table.get(p);
+ Integer p = pos;
+ Mark m = table.get(p);
if (m == null) {
m = new Mark(pos);
table.put(p, m);
@@ -205,13 +222,13 @@ public class BasicBlock {
return m;
}
- private HashMap makeMarks(CodeIterator ci, int begin, int end,
+ private Map<Integer,Mark> makeMarks(CodeIterator ci, int begin, int end,
ExceptionTable et)
throws BadBytecode
{
ci.begin();
ci.move(begin);
- HashMap marks = new HashMap();
+ Map<Integer,Mark> marks = new HashMap<Integer,Mark>();
while (ci.hasNext()) {
int index = ci.next();
if (index >= end)
@@ -273,7 +290,7 @@ public class BasicBlock {
else if (op == Opcode.JSR_W)
makeJsr(marks, index, index + ci.s32bitAt(index + 1), 5);
else if (op == Opcode.WIDE && ci.byteAt(index + 1) == Opcode.RET)
- makeMark(marks, index, null, 1, true);
+ makeMark(marks, index, null, 4, true);
}
if (et != null) {
@@ -287,29 +304,30 @@ public class BasicBlock {
return marks;
}
- private void makeGoto(HashMap marks, int pos, int target, int size) {
+ private void makeGoto(Map<Integer,Mark> marks, int pos, int target, int size) {
Mark to = makeMark(marks, target);
BasicBlock[] jumps = makeArray(to.block);
makeMark(marks, pos, jumps, size, true);
}
- /**
- * We ignore JSR since Java 6 or later does not allow it.
- */
- protected void makeJsr(HashMap marks, int pos, int target, int size) {
/*
+ * We could ignore JSR since Java 7 or later does not allow it.
+ * See The JVM Spec. Sec. 4.10.2.5.
+ */
+ protected void makeJsr(Map<Integer,Mark> marks, int pos, int target, int size) throws BadBytecode {
+ /*
Mark to = makeMark(marks, target);
Mark next = makeMark(marks, pos + size);
BasicBlock[] jumps = makeArray(to.block, next.block);
makeMark(marks, pos, jumps, size, false);
- */
+ */
+ throw new JsrBytecode();
}
- private BasicBlock[] makeBlocks(HashMap markTable) {
- Mark[] marks = (Mark[])markTable.values()
- .toArray(new Mark[markTable.size()]);
- java.util.Arrays.sort(marks);
- ArrayList blocks = new ArrayList();
+ private BasicBlock[] makeBlocks(Map<Integer,Mark> markTable) {
+ Mark[] marks = markTable.values().toArray(new Mark[markTable.size()]);
+ Arrays.sort(marks);
+ List<BasicBlock> blocks = new ArrayList<BasicBlock>();
int i = 0;
BasicBlock prev;
if (marks.length > 0 && marks[0].position == 0 && marks[0].block != null)
@@ -342,12 +360,14 @@ public class BasicBlock {
}
else {
// the previous mark already has exits.
- int prevPos = prev.position;
- if (prevPos + prev.length < m.position) {
- prev = makeBlock(prevPos + prev.length);
- prev.length = m.position - prevPos;
+ if (prev.position + prev.length < m.position) {
+ // dead code is found.
+ prev = makeBlock(prev.position + prev.length);
+ blocks.add(prev);
+ prev.length = m.position - prev.position;
// the incoming flow from dead code is not counted
// bb.incoming++;
+ prev.stop = true; // because the incoming flow is not counted.
prev.exit = makeArray(bb);
}
}
@@ -357,7 +377,7 @@ public class BasicBlock {
}
}
- return (BasicBlock[])blocks.toArray(makeArray(blocks.size()));
+ return blocks.toArray(makeArray(blocks.size()));
}
private static BasicBlock getBBlock(Mark m) {
diff --git a/src/main/javassist/bytecode/stackmap/Liveness.java b/src/main/javassist/bytecode/stackmap/Liveness.java
deleted file mode 100644
index 4acd65e..0000000
--- a/src/main/javassist/bytecode/stackmap/Liveness.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- */
-
- package javassist.bytecode.stackmap;
-
-import javassist.bytecode.*;
-
-public class Liveness {
- protected static final byte UNKNOWN = 0;
- protected static final byte READ = 1;
- protected static final byte UPDATED = 2;
- protected byte[] localsUsage;
-
- /**
- * If true, all the arguments become alive within the whole method body.
- *
- * To correctly compute a stack map table, all the arguments must
- * be alive (localsUsage[?] must be READ) at least in the first block.
- */
- public static boolean useArgs = true;
-
- public void compute(CodeIterator ci, TypedBlock[] blocks, int maxLocals,
- TypeData[] args)
- throws BadBytecode
- {
- computeUsage(ci, blocks, maxLocals);
- if (useArgs)
- useAllArgs(blocks, args);
-
- computeLiveness1(blocks[0]);
- while (hasChanged(blocks))
- computeLiveness2(blocks[0]);
- }
-
- private void useAllArgs(TypedBlock[] blocks, TypeData[] args) {
- for (int k = 0; k < blocks.length; k++) {
- byte[] usage = blocks[k].localsUsage;
- for (int i = 0; i < args.length; i++)
- if (args[i] != TypeTag.TOP)
- usage[i] = READ;
- }
- }
-
- static final int NOT_YET = 0;
- static final int CHANGED_LAST = 1;
- static final int DONE = 2;
- static final int CHANGED_NOW = 3;
-
- private void computeLiveness1(TypedBlock tb) {
- if (tb.updating) {
- // a loop was detected.
- computeLiveness1u(tb);
- return;
- }
-
- if (tb.inputs != null)
- return;
-
- tb.updating = true;
- byte[] usage = tb.localsUsage;
- int n = usage.length;
- boolean[] in = new boolean[n];
- for (int i = 0; i < n; i++)
- in[i] = usage[i] == READ;
-
- BasicBlock.Catch handlers = tb.toCatch;
- while (handlers != null) {
- TypedBlock h = (TypedBlock)handlers.body;
- computeLiveness1(h);
- for (int k = 0; k < n; k++)
- if (h.inputs[k])
- in[k] = true;
-
- handlers = handlers.next;
- }
-
- if (tb.exit != null) {
- for (int i = 0; i < tb.exit.length; i++) {
- TypedBlock e = (TypedBlock)tb.exit[i];
- computeLiveness1(e);
- for (int k = 0; k < n; k++)
- if (!in[k])
- in[k] = usage[k] == UNKNOWN && e.inputs[k];
- }
- }
-
- tb.updating = false;
- if (tb.inputs == null) {
- tb.inputs = in;
- tb.status = DONE;
- }
- else {
- for (int i = 0; i < n; i++)
- if (in[i] && !tb.inputs[i]) {
- tb.inputs[i] = true;
- tb.status = CHANGED_NOW;
- }
- }
- }
-
- private void computeLiveness1u(TypedBlock tb) {
- if (tb.inputs == null) {
- byte[] usage = tb.localsUsage;
- int n = usage.length;
- boolean[] in = new boolean[n];
- for (int i = 0; i < n; i++)
- in[i] = usage[i] == READ;
-
- tb.inputs = in;
- tb.status = DONE;
- }
- }
-
- private void computeLiveness2(TypedBlock tb) {
- if (tb.updating || tb.status >= DONE)
- return;
-
- tb.updating = true;
- if (tb.exit == null)
- tb.status = DONE;
- else {
- boolean changed = false;
- for (int i = 0; i < tb.exit.length; i++) {
- TypedBlock e = (TypedBlock)tb.exit[i];
- computeLiveness2(e);
- if (e.status != DONE)
- changed = true;
- }
-
- if (changed) {
- changed = false;
- byte[] usage = tb.localsUsage;
- int n = usage.length;
- for (int i = 0; i < tb.exit.length; i++) {
- TypedBlock e = (TypedBlock)tb.exit[i];
- if (e.status != DONE)
- for (int k = 0; k < n; k++)
- if (!tb.inputs[k]) {
- if (usage[k] == UNKNOWN && e.inputs[k]) {
- tb.inputs[k] = true;
- changed = true;
- }
- }
- }
-
- tb.status = changed ? CHANGED_NOW : DONE;
- }
- else
- tb.status = DONE;
- }
-
- if (computeLiveness2except(tb))
- tb.status = CHANGED_NOW;
-
- tb.updating = false;
- }
-
- private boolean computeLiveness2except(TypedBlock tb) {
- BasicBlock.Catch handlers = tb.toCatch;
- boolean changed = false;
- while (handlers != null) {
- TypedBlock h = (TypedBlock)handlers.body;
- computeLiveness2(h);
- if (h.status != DONE) {
- boolean[] in = tb.inputs;
- int n = in.length;
- for (int k = 0; k < n; k++)
- if (!in[k] && h.inputs[k]) {
- in[k] = true;
- changed = true;
- }
- }
-
- handlers = handlers.next;
- }
-
- return changed;
- }
-
- private boolean hasChanged(TypedBlock[] blocks) {
- int n = blocks.length;
- boolean changed = false;
- for (int i = 0; i < n; i++) {
- TypedBlock tb = blocks[i];
- if (tb.status == CHANGED_NOW) {
- tb.status = CHANGED_LAST;
- changed = true;
- }
- else
- tb.status = NOT_YET;
- }
-
- return changed;
- }
-
- private void computeUsage(CodeIterator ci, TypedBlock[] blocks, int maxLocals)
- throws BadBytecode
- {
- int n = blocks.length;
- for (int i = 0; i < n; i++) {
- TypedBlock tb = blocks[i];
- localsUsage = tb.localsUsage = new byte[maxLocals];
- int pos = tb.position;
- analyze(ci, pos, pos + tb.length);
- localsUsage = null;
- }
- }
-
- protected final void readLocal(int reg) {
- if (localsUsage[reg] == UNKNOWN)
- localsUsage[reg] = READ;
- }
-
- protected final void writeLocal(int reg) {
- if (localsUsage[reg] == UNKNOWN)
- localsUsage[reg] = UPDATED;
- }
-
- protected void analyze(CodeIterator ci, int begin, int end)
- throws BadBytecode
- {
- ci.begin();
- ci.move(begin);
- while (ci.hasNext()) {
- int index = ci.next();
- if (index >= end)
- break;
-
- int op = ci.byteAt(index);
- if (op < 96)
- if (op < 54)
- doOpcode0_53(ci, index, op);
- else
- doOpcode54_95(ci, index, op);
- else
- if (op == Opcode.IINC) {
- // this does not call writeLocal().
- readLocal(ci.byteAt(index + 1));
- }
- else if (op == Opcode.WIDE)
- doWIDE(ci, index);
- }
- }
-
- private void doOpcode0_53(CodeIterator ci, int pos, int op) {
- switch (op) {
- case Opcode.ILOAD :
- case Opcode.LLOAD :
- case Opcode.FLOAD :
- case Opcode.DLOAD :
- case Opcode.ALOAD :
- readLocal(ci.byteAt(pos + 1));
- break;
- case Opcode.ILOAD_0 :
- case Opcode.ILOAD_1 :
- case Opcode.ILOAD_2 :
- case Opcode.ILOAD_3 :
- readLocal(op - Opcode.ILOAD_0);
- break;
- case Opcode.LLOAD_0 :
- case Opcode.LLOAD_1 :
- case Opcode.LLOAD_2 :
- case Opcode.LLOAD_3 :
- readLocal(op - Opcode.LLOAD_0);
- break;
- case Opcode.FLOAD_0 :
- case Opcode.FLOAD_1 :
- case Opcode.FLOAD_2 :
- case Opcode.FLOAD_3 :
- readLocal(op - Opcode.FLOAD_0);
- break;
- case Opcode.DLOAD_0 :
- case Opcode.DLOAD_1 :
- case Opcode.DLOAD_2 :
- case Opcode.DLOAD_3 :
- readLocal(op - Opcode.DLOAD_0);
- break;
- case Opcode.ALOAD_0 :
- case Opcode.ALOAD_1 :
- case Opcode.ALOAD_2 :
- case Opcode.ALOAD_3 :
- readLocal(op - Opcode.ALOAD_0);
- break;
- }
- }
-
- private void doOpcode54_95(CodeIterator ci, int pos, int op) {
- switch (op) {
- case Opcode.ISTORE :
- case Opcode.LSTORE :
- case Opcode.FSTORE :
- case Opcode.DSTORE :
- case Opcode.ASTORE :
- writeLocal(ci.byteAt(pos + 1));
- break;
- case Opcode.ISTORE_0 :
- case Opcode.ISTORE_1 :
- case Opcode.ISTORE_2 :
- case Opcode.ISTORE_3 :
- writeLocal(op - Opcode.ISTORE_0);
- break;
- case Opcode.LSTORE_0 :
- case Opcode.LSTORE_1 :
- case Opcode.LSTORE_2 :
- case Opcode.LSTORE_3 :
- writeLocal(op - Opcode.LSTORE_0);
- break;
- case Opcode.FSTORE_0 :
- case Opcode.FSTORE_1 :
- case Opcode.FSTORE_2 :
- case Opcode.FSTORE_3 :
- writeLocal(op - Opcode.FSTORE_0);
- break;
- case Opcode.DSTORE_0 :
- case Opcode.DSTORE_1 :
- case Opcode.DSTORE_2 :
- case Opcode.DSTORE_3 :
- writeLocal(op - Opcode.DSTORE_0);
- break;
- case Opcode.ASTORE_0 :
- case Opcode.ASTORE_1 :
- case Opcode.ASTORE_2 :
- case Opcode.ASTORE_3 :
- writeLocal(op - Opcode.ASTORE_0);
- break;
- }
- }
-
- private void doWIDE(CodeIterator ci, int pos) throws BadBytecode {
- int op = ci.byteAt(pos + 1);
- int var = ci.u16bitAt(pos + 2);
- switch (op) {
- case Opcode.ILOAD :
- case Opcode.LLOAD :
- case Opcode.FLOAD :
- case Opcode.DLOAD :
- case Opcode.ALOAD :
- readLocal(var);
- break;
- case Opcode.ISTORE :
- case Opcode.LSTORE :
- case Opcode.FSTORE :
- case Opcode.DSTORE :
- case Opcode.ASTORE :
- writeLocal(var);
- break;
- case Opcode.IINC :
- readLocal(var);
- // this does not call writeLocal().
- break;
- }
- }
-}
diff --git a/src/main/javassist/bytecode/stackmap/MapMaker.java b/src/main/javassist/bytecode/stackmap/MapMaker.java
index 92cd37c..bd79377 100644
--- a/src/main/javassist/bytecode/stackmap/MapMaker.java
+++ b/src/main/javassist/bytecode/stackmap/MapMaker.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,8 +16,19 @@
package javassist.bytecode.stackmap;
+import java.util.ArrayList;
+import java.util.List;
+
import javassist.ClassPool;
-import javassist.bytecode.*;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.ByteArray;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.StackMap;
+import javassist.bytecode.StackMapTable;
/**
* Stack map maker.
@@ -79,7 +91,7 @@ public class MapMaker extends Tracer {
/**
* Computes the stack map table of the given method and returns it.
* It returns null if the given method does not have to have a
- * stack map table.
+ * stack map table or it includes JSR.
*/
public static StackMapTable make(ClassPool classes, MethodInfo minfo)
throws BadBytecode
@@ -88,19 +100,32 @@ public class MapMaker extends Tracer {
if (ca == null)
return null;
- TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, true);
+ TypedBlock[] blocks;
+ try {
+ blocks = TypedBlock.makeBlocks(minfo, ca, true);
+ }
+ catch (BasicBlock.JsrBytecode e) {
+ return null;
+ }
+
if (blocks == null)
return null;
MapMaker mm = new MapMaker(classes, minfo, ca);
- mm.make(blocks, ca.getCode());
+ try {
+ mm.make(blocks, ca.getCode());
+ }
+ catch (BadBytecode bb) {
+ throw new BadBytecode(minfo, bb);
+ }
+
return mm.toStackMap(blocks);
}
/**
* Computes the stack map table for J2ME.
* It returns null if the given method does not have to have a
- * stack map table.
+ * stack map table or it includes JSR.
*/
public static StackMap make2(ClassPool classes, MethodInfo minfo)
throws BadBytecode
@@ -109,12 +134,24 @@ public class MapMaker extends Tracer {
if (ca == null)
return null;
- TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, true);
+ TypedBlock[] blocks;
+ try {
+ blocks = TypedBlock.makeBlocks(minfo, ca, true);
+ }
+ catch (BasicBlock.JsrBytecode e) {
+ return null;
+ }
+
if (blocks == null)
return null;
MapMaker mm = new MapMaker(classes, minfo, ca);
- mm.make(blocks, ca.getCode());
+ try {
+ mm.make(blocks, ca.getCode());
+ }
+ catch (BadBytecode bb) {
+ throw new BadBytecode(minfo, bb);
+ }
return mm.toStackMap2(minfo.getConstPool(), blocks);
}
@@ -124,9 +161,7 @@ public class MapMaker extends Tracer {
TypedBlock.getRetType(minfo.getDescriptor()));
}
- protected MapMaker(MapMaker old, boolean copyStack) {
- super(old, copyStack);
- }
+ protected MapMaker(MapMaker old) { super(old); }
/**
* Runs an analyzer (Phase 1 and 2).
@@ -134,34 +169,12 @@ public class MapMaker extends Tracer {
void make(TypedBlock[] blocks, byte[] code)
throws BadBytecode
{
- TypedBlock first = blocks[0];
- fixParamTypes(first);
- TypeData[] srcTypes = first.localsTypes;
- copyFrom(srcTypes.length, srcTypes, this.localsTypes);
- make(code, first);
-
- int n = blocks.length;
- for (int i = 0; i < n; i++)
- evalExpected(blocks[i]);
- }
-
- /*
- * If a parameter type is String but it is used only as Object
- * within the method body, this MapMaker class will report its type
- * is Object. To avoid this, fixParamTypes calls TypeData.setType()
- * on each parameter type.
- */
- private void fixParamTypes(TypedBlock first) throws BadBytecode {
- TypeData[] types = first.localsTypes;
- int n = types.length;
- for (int i = 0; i < n; i++) {
- TypeData t = types[i];
- if (t instanceof TypeData.ClassName) {
- /* Skip the following statement if t.isNullType() is true
- * although a parameter type is never null type.
- */
- TypeData.setType(t, t.getName(), classPool);
- }
+ make(code, blocks[0]);
+ findDeadCatchers(code, blocks);
+ try {
+ fixTypes(code, blocks);
+ } catch (NotFoundException e) {
+ throw new BadBytecode("failed to resolve types", e);
}
}
@@ -170,16 +183,18 @@ public class MapMaker extends Tracer {
private void make(byte[] code, TypedBlock tb)
throws BadBytecode
{
- BasicBlock.Catch handlers = tb.toCatch;
- while (handlers != null) {
- traceException(code, handlers);
- handlers = handlers.next;
- }
+ copyTypeData(tb.stackTop, tb.stackTypes, stackTypes);
+ stackTop = tb.stackTop;
+ copyTypeData(tb.localsTypes.length, tb.localsTypes, localsTypes);
+
+ traceException(code, tb.toCatch);
int pos = tb.position;
int end = pos + tb.length;
- while (pos < end)
+ while (pos < end) {
pos += doOpcode(pos, code);
+ traceException(code, tb.toCatch);
+ }
if (tb.exit != null) {
for (int i = 0; i < tb.exit.length; i++) {
@@ -188,7 +203,7 @@ public class MapMaker extends Tracer {
mergeMap(e, true);
else {
recordStackMap(e);
- MapMaker maker = new MapMaker(this, true);
+ MapMaker maker = new MapMaker(this);
maker.make(code, e);
}
}
@@ -198,106 +213,194 @@ public class MapMaker extends Tracer {
private void traceException(byte[] code, TypedBlock.Catch handler)
throws BadBytecode
{
- TypedBlock tb = (TypedBlock)handler.body;
- if (tb.alreadySet())
- mergeMap(tb, false);
- else {
- recordStackMap(tb, handler.typeIndex);
- MapMaker maker = new MapMaker(this, false);
-
- /* the following code is equivalent to maker.copyFrom(this)
- * except stackTypes are not copied.
- */
- maker.stackTypes[0] = tb.stackTypes[0].getSelf();
- maker.stackTop = 1;
- maker.make(code, tb);
+ while (handler != null) {
+ TypedBlock tb = (TypedBlock)handler.body;
+ if (tb.alreadySet()) {
+ mergeMap(tb, false);
+ if (tb.stackTop < 1)
+ throw new BadBytecode("bad catch clause: " + handler.typeIndex);
+
+ tb.stackTypes[0] = merge(toExceptionType(handler.typeIndex),
+ tb.stackTypes[0]);
+ }
+ else {
+ recordStackMap(tb, handler.typeIndex);
+ MapMaker maker = new MapMaker(this);
+ maker.make(code, tb);
+ }
+
+ handler = handler.next;
}
}
- private void mergeMap(TypedBlock dest, boolean mergeStack) {
- boolean[] inputs = dest.inputs;
- int n = inputs.length;
+ private void mergeMap(TypedBlock dest, boolean mergeStack) throws BadBytecode {
+ int n = localsTypes.length;
for (int i = 0; i < n; i++)
- if (inputs[i])
- merge(localsTypes[i], dest.localsTypes[i]);
+ dest.localsTypes[i] = merge(validateTypeData(localsTypes, n, i),
+ dest.localsTypes[i]);
if (mergeStack) {
n = stackTop;
for (int i = 0; i < n; i++)
- merge(stackTypes[i], dest.stackTypes[i]);
+ dest.stackTypes[i] = merge(stackTypes[i], dest.stackTypes[i]);
}
}
- private void merge(TypeData td, TypeData target) {
- boolean tdIsObj = false;
- boolean targetIsObj = false;
- // td or target is null if it is TOP.
- if (td != TOP && td.isObjectType())
- tdIsObj = true;
-
- if (target != TOP && target.isObjectType())
- targetIsObj = true;
-
- if (tdIsObj && targetIsObj)
- target.merge(td);
+ private TypeData merge(TypeData src, TypeData target) throws BadBytecode {
+ if (src == target)
+ return target;
+ else if (target instanceof TypeData.ClassName
+ || target instanceof TypeData.BasicType) // a parameter
+ return target;
+ else if (target instanceof TypeData.AbsTypeVar) {
+ ((TypeData.AbsTypeVar)target).merge(src);
+ return target;
+ }
+ else
+ throw new RuntimeException("fatal: this should never happen");
}
private void recordStackMap(TypedBlock target)
throws BadBytecode
{
- TypeData[] tStackTypes = new TypeData[stackTypes.length];
+ TypeData[] tStackTypes = TypeData.make(stackTypes.length);
int st = stackTop;
- copyFrom(st, stackTypes, tStackTypes);
+ recordTypeData(st, stackTypes, tStackTypes);
recordStackMap0(target, st, tStackTypes);
}
private void recordStackMap(TypedBlock target, int exceptionType)
throws BadBytecode
{
+ TypeData[] tStackTypes = TypeData.make(stackTypes.length);
+ tStackTypes[0] = toExceptionType(exceptionType).join();
+ recordStackMap0(target, 1, tStackTypes);
+ }
+
+ private TypeData.ClassName toExceptionType(int exceptionType) {
String type;
- if (exceptionType == 0)
- type = "java.lang.Throwable";
+ if (exceptionType == 0) // for finally clauses
+ type= "java.lang.Throwable";
else
type = cpool.getClassInfo(exceptionType);
- TypeData[] tStackTypes = new TypeData[stackTypes.length];
- tStackTypes[0] = new TypeData.ClassName(type);
-
- recordStackMap0(target, 1, tStackTypes);
+ return new TypeData.ClassName(type);
}
private void recordStackMap0(TypedBlock target, int st, TypeData[] tStackTypes)
throws BadBytecode
{
int n = localsTypes.length;
- TypeData[] tLocalsTypes = new TypeData[n];
- int k = copyFrom(n, localsTypes, tLocalsTypes);
+ TypeData[] tLocalsTypes = TypeData.make(n);
+ int k = recordTypeData(n, localsTypes, tLocalsTypes);
+ target.setStackMap(st, tStackTypes, k, tLocalsTypes);
+ }
+
+ protected static int recordTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) {
+ int k = -1;
+ for (int i = 0; i < n; i++) {
+ TypeData t = validateTypeData(srcTypes, n, i);
+ destTypes[i] = t.join();
+ if (t != TOP)
+ k = i + 1; // t might be long or double.
+ }
+
+ return k + 1;
+ }
- boolean[] inputs = target.inputs;
+ protected static void copyTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) {
for (int i = 0; i < n; i++)
- if (!inputs[i])
- tLocalsTypes[i] = TOP;
+ destTypes[i] = srcTypes[i];
+ }
- target.setStackMap(st, tStackTypes, k, tLocalsTypes);
+ private static TypeData validateTypeData(TypeData[] data, int length, int index) {
+ TypeData td = data[index];
+ if (td.is2WordType() && index + 1 < length)
+ if (data[index + 1] != TOP)
+ return TOP;
+
+ return td;
}
- // Phase 2
+ // Phase 1.5
+
+ /*
+ * Javac may generate an exception handler that catches only the exception
+ * thrown within the handler itself. It is dead code.
+ * See javassist.JvstTest4.testJIRA195().
+ */
- void evalExpected(TypedBlock target) throws BadBytecode {
- ClassPool cp = classPool;
- evalExpected(cp, target.stackTop, target.stackTypes);
- TypeData[] types = target.localsTypes;
- if (types != null) // unless this block is dead code
- evalExpected(cp, types.length, types);
+ private void findDeadCatchers(byte[] code, TypedBlock[] blocks) throws BadBytecode {
+ int len = blocks.length;
+ for (int i = 0; i < len; i++) {
+ TypedBlock block = blocks[i];
+ if (!block.alreadySet()) {
+ fixDeadcode(code, block);
+ BasicBlock.Catch handler = block.toCatch;
+ if (handler != null) {
+ TypedBlock tb = (TypedBlock)handler.body;
+ if (!tb.alreadySet()) {
+ // tb is a handler that catches only the exceptions
+ // thrown from dead code.
+ recordStackMap(tb, handler.typeIndex);
+ fixDeadcode(code, tb);
+ tb.incoming = 1;
+ }
+ }
+
+ }
+ }
}
- private static void evalExpected(ClassPool cp, int n, TypeData[] types)
- throws BadBytecode
- {
- for (int i = 0; i < n; i++) {
- TypeData td = types[i];
- if (td != null)
- td.evalExpectedType(cp);
+ private void fixDeadcode(byte[] code, TypedBlock block) throws BadBytecode {
+ int pos = block.position;
+ int len = block.length - 3;
+ if (len < 0) {
+ // if the dead-code length is shorter than 3 bytes.
+ if (len == -1)
+ code[pos] = Bytecode.NOP;
+
+ code[pos + block.length - 1] = (byte)Bytecode.ATHROW;
+ block.incoming = 1;
+ recordStackMap(block, 0);
+ return;
+ }
+
+ // if block.incomping > 0, all the incoming edges are from
+ // other dead code blocks. So set block.incoming to 0.
+ block.incoming = 0;
+
+ for (int k = 0; k < len; k++)
+ code[pos + k] = Bytecode.NOP;
+
+ code[pos + len] = (byte)Bytecode.GOTO;
+ ByteArray.write16bit(-len, code, pos + len + 1);
+ }
+
+ // Phase 2
+
+ /*
+ * This method first finds strongly connected components (SCCs)
+ * in a TypeData graph by Tarjan's algorithm.
+ * SCCs are TypeData nodes sharing the same type.
+ * Since SCCs are found in the topologically sorted order,
+ * their types are also fixed when they are found.
+ */
+ private void fixTypes(byte[] code, TypedBlock[] blocks) throws NotFoundException, BadBytecode {
+ List<TypeData> preOrder = new ArrayList<TypeData>();
+ int len = blocks.length;
+ int index = 0;
+ for (int i = 0; i < len; i++) {
+ TypedBlock block = blocks[i];
+ if (block.alreadySet()) { // if block is not dead code
+ int n = block.localsTypes.length;
+ for (int j = 0; j < n; j++)
+ index = block.localsTypes[j].dfs(preOrder, index, classPool);
+
+ n = block.stackTop;
+ for (int j = 0; j < n; j++)
+ index = block.stackTypes[j].dfs(preOrder, index, classPool);
+ }
}
}
@@ -323,6 +426,11 @@ public class MapMaker extends Tracer {
offsetDelta = bb.length - 1;
prev = bb;
}
+ else if (bb.incoming == 0) {
+ // dead code.
+ writer.sameFrame(offsetDelta);
+ offsetDelta = bb.length - 1;
+ }
else
offsetDelta += bb.length;
}
@@ -369,19 +477,14 @@ public class MapMaker extends Tracer {
}
else if (stackTop == 1 && diffL == 0) {
TypeData td = bb.stackTypes[0];
- if (td == TOP)
- writer.sameLocals(offsetDelta, StackMapTable.TOP, 0);
- else
- writer.sameLocals(offsetDelta, td.getTypeTag(),
- td.getTypeData(cpool));
+ writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool));
return;
}
else if (stackTop == 2 && diffL == 0) {
TypeData td = bb.stackTypes[0];
- if (td != TOP && td.is2WordType()) {
+ if (td.is2WordType()) {
// bb.stackTypes[1] must be TOP.
- writer.sameLocals(offsetDelta, td.getTypeTag(),
- td.getTypeData(cpool));
+ writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool));
return;
}
}
@@ -400,16 +503,10 @@ public class MapMaker extends Tracer {
int j = 0;
for (int i = 0; i < num; i++) {
TypeData td = types[offset + i];
- if (td == TOP) {
- tags[j] = StackMapTable.TOP;
- data[j] = 0;
- }
- else {
- tags[j] = td.getTypeTag();
- data[j] = td.getTypeData(cp);
- if (td.is2WordType())
- i++;
- }
+ tags[j] = td.getTypeTag();
+ data[j] = td.getTypeData(cp);
+ if (td.is2WordType())
+ i++;
j++;
}
@@ -432,20 +529,13 @@ public class MapMaker extends Tracer {
return diffSize(newTd, len, newTdLen);
else
return -diffSize(oldTd, len, oldTdLen);
- else
- return -100;
+ return -100;
}
private static boolean stackMapEq(TypeData[] oldTd, TypeData[] newTd, int len) {
for (int i = 0; i < len; i++) {
- TypeData td = oldTd[i];
- if (td == TOP) { // the next element to LONG/DOUBLE is TOP.
- if (newTd[i] != TOP)
- return false;
- }
- else
- if (!oldTd[i].equals(newTd[i]))
- return false;
+ if (!oldTd[i].eq(newTd[i]))
+ return false;
}
return true;
@@ -456,7 +546,7 @@ public class MapMaker extends Tracer {
while (offset < len) {
TypeData td = types[offset++];
num++;
- if (td != TOP && td.is2WordType())
+ if (td.is2WordType())
offset++;
}
@@ -514,13 +604,9 @@ public class MapMaker extends Tracer {
writer.write16bit(num - numDWord);
for (int i = 0; i < num; i++) {
TypeData td = types[i];
- if (td == TOP)
- writer.writeVerifyTypeInfo(StackMap.TOP, 0);
- else {
- writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp));
- if (td.is2WordType())
- i++;
- }
+ writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp));
+ if (td.is2WordType())
+ i++;
}
}
}
diff --git a/src/main/javassist/bytecode/stackmap/Tracer.java b/src/main/javassist/bytecode/stackmap/Tracer.java
index 89e788d..6f99e5f 100644
--- a/src/main/javassist/bytecode/stackmap/Tracer.java
+++ b/src/main/javassist/bytecode/stackmap/Tracer.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,12 +16,12 @@
package javassist.bytecode.stackmap;
+import javassist.ClassPool;
+import javassist.bytecode.BadBytecode;
import javassist.bytecode.ByteArray;
-import javassist.bytecode.Opcode;
import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
-import javassist.bytecode.BadBytecode;
-import javassist.ClassPool;
+import javassist.bytecode.Opcode;
/*
* A class for performing abstract interpretation.
@@ -30,7 +31,7 @@ import javassist.ClassPool;
public abstract class Tracer implements TypeTag {
protected ClassPool classPool;
protected ConstPool cpool;
- protected String returnType;
+ protected String returnType; // used as the type of ARETURN
protected int stackTop;
protected TypeData[] stackTypes;
@@ -42,39 +43,17 @@ public abstract class Tracer implements TypeTag {
cpool = cp;
returnType = retType;
stackTop = 0;
- stackTypes = new TypeData[maxStack];
- localsTypes = new TypeData[maxLocals];
+ stackTypes = TypeData.make(maxStack);
+ localsTypes = TypeData.make(maxLocals);
}
- public Tracer(Tracer t, boolean copyStack) {
+ public Tracer(Tracer t) {
classPool = t.classPool;
cpool = t.cpool;
returnType = t.returnType;
-
stackTop = t.stackTop;
- int size = t.stackTypes.length;
- stackTypes = new TypeData[size];
- if (copyStack)
- copyFrom(t.stackTop, t.stackTypes, stackTypes);
-
- int size2 = t.localsTypes.length;
- localsTypes = new TypeData[size2];
- copyFrom(size2, t.localsTypes, localsTypes);
- }
-
- protected static int copyFrom(int n, TypeData[] srcTypes, TypeData[] destTypes) {
- int k = -1;
- for (int i = 0; i < n; i++) {
- TypeData t = srcTypes[i];
- destTypes[i] = t == TOP ? TOP : t.getSelf();
- if (t != TOP)
- if (t.is2WordType())
- k = i + 1;
- else
- k = i;
- }
-
- return k + 1;
+ stackTypes = TypeData.make(t.stackTypes.length);
+ localsTypes = TypeData.make(t.localsTypes.length);
}
/**
@@ -90,19 +69,16 @@ public abstract class Tracer implements TypeTag {
protected int doOpcode(int pos, byte[] code) throws BadBytecode {
try {
int op = code[pos] & 0xff;
+ if (op < 54)
+ return doOpcode0_53(pos, code, op);
if (op < 96)
- if (op < 54)
- return doOpcode0_53(pos, code, op);
- else
- return doOpcode54_95(pos, code, op);
- else
- if (op < 148)
- return doOpcode96_147(pos, code, op);
- else
- return doOpcode148_201(pos, code, op);
+ return doOpcode54_95(pos, code, op);
+ if (op < 148)
+ return doOpcode96_147(pos, code, op);
+ return doOpcode148_201(pos, code, op);
}
catch (ArrayIndexOutOfBoundsException e) {
- throw new BadBytecode("inconsistent stack height " + e.getMessage());
+ throw new BadBytecode("inconsistent stack height " + e.getMessage(), e);
}
}
@@ -125,7 +101,7 @@ public abstract class Tracer implements TypeTag {
* @param pos the position of LOOKUPSWITCH
* @param code bytecode
* @param n the number of case labels
- * @param offsetPos the position of the table of pairs of a value and a branch target.
+ * @param pairsPos the position of the table of pairs of a value and a branch target.
* @param defaultOffset the offset to the default branch target.
*/
protected void visitLookupSwitch(int pos, byte[] code, int n,
@@ -254,11 +230,7 @@ public abstract class Tracer implements TypeTag {
case Opcode.AALOAD : {
int s = --stackTop - 1;
TypeData data = stackTypes[s];
- if (data == null || !data.isObjectType())
- throw new BadBytecode("bad AALOAD");
- else
- stackTypes[s] = new TypeData.ArrayElement(data);
-
+ stackTypes[s] = TypeData.ArrayElement.make(data);
break; }
case Opcode.BALOAD :
case Opcode.CALOAD :
@@ -308,14 +280,12 @@ public abstract class Tracer implements TypeTag {
return 2;
}
- private int doALOAD(int localVar) { // int localVar, TypeData type) {
+ private int doALOAD(int localVar) {
stackTypes[stackTop++] = localsTypes[localVar];
return 2;
}
private int doOpcode54_95(int pos, byte[] code, int op) throws BadBytecode {
- TypeData[] localsTypes = this.localsTypes;
- TypeData[] stackTypes = this.stackTypes;
switch (op) {
case Opcode.ISTORE :
return doXSTORE(pos, code, INTEGER);
@@ -375,9 +345,9 @@ public abstract class Tracer implements TypeTag {
stackTop -= (op == Opcode.LASTORE || op == Opcode.DASTORE) ? 4 : 3;
break;
case Opcode.AASTORE :
- TypeData.setType(stackTypes[stackTop - 1],
- TypeData.ArrayElement.getElementType(stackTypes[stackTop - 3].getName()),
- classPool);
+ TypeData.ArrayElement.aastore(stackTypes[stackTop - 3],
+ stackTypes[stackTop - 1],
+ classPool);
stackTop -= 3;
break;
case Opcode.BASTORE :
@@ -449,7 +419,7 @@ public abstract class Tracer implements TypeTag {
private int doASTORE(int index) {
stackTop--;
// implicit upcast might be done.
- localsTypes[index] = stackTypes[stackTop].copy();
+ localsTypes[index] = stackTypes[stackTop];
return 2;
}
@@ -474,16 +444,16 @@ public abstract class Tracer implements TypeTag {
// this does not call writeLocal().
return 3;
case Opcode.I2L :
- stackTypes[stackTop] = LONG;
- stackTypes[stackTop - 1] = TOP;
+ stackTypes[stackTop - 1] = LONG;
+ stackTypes[stackTop] = TOP;
stackTop++;
break;
case Opcode.I2F :
stackTypes[stackTop - 1] = FLOAT;
break;
case Opcode.I2D :
- stackTypes[stackTop] = DOUBLE;
- stackTypes[stackTop - 1] = TOP;
+ stackTypes[stackTop - 1] = DOUBLE;
+ stackTypes[stackTop] = TOP;
stackTop++;
break;
case Opcode.L2I :
@@ -493,24 +463,26 @@ public abstract class Tracer implements TypeTag {
stackTypes[--stackTop - 1] = FLOAT;
break;
case Opcode.L2D :
- stackTypes[stackTop - 1] = DOUBLE;
+ stackTypes[stackTop - 2] = DOUBLE;
break;
case Opcode.F2I :
stackTypes[stackTop - 1] = INTEGER;
break;
case Opcode.F2L :
- stackTypes[stackTop - 1] = TOP;
- stackTypes[stackTop++] = LONG;
+ stackTypes[stackTop - 1] = LONG;
+ stackTypes[stackTop] = TOP;
+ stackTop++;
break;
case Opcode.F2D :
- stackTypes[stackTop - 1] = TOP;
- stackTypes[stackTop++] = DOUBLE;
+ stackTypes[stackTop - 1] = DOUBLE;
+ stackTypes[stackTop] = TOP;
+ stackTop++;
break;
case Opcode.D2I :
stackTypes[--stackTop - 1] = INTEGER;
break;
case Opcode.D2L :
- stackTypes[stackTop - 1] = LONG;
+ stackTypes[stackTop - 2] = LONG;
break;
case Opcode.D2F :
stackTypes[--stackTop - 1] = FLOAT;
@@ -601,7 +573,7 @@ public abstract class Tracer implements TypeTag {
visitReturn(pos, code);
break;
case Opcode.ARETURN :
- TypeData.setType(stackTypes[--stackTop], returnType, classPool);
+ stackTypes[--stackTop].setType(returnType, classPool);
visitReturn(pos, code);
break;
case Opcode.RETURN :
@@ -622,8 +594,8 @@ public abstract class Tracer implements TypeTag {
return doInvokeMethod(pos, code, false);
case Opcode.INVOKEINTERFACE :
return doInvokeIntfMethod(pos, code);
- case 186 :
- throw new RuntimeException("bad opcode 186");
+ case Opcode.INVOKEDYNAMIC :
+ return doInvokeDynamic(pos, code);
case Opcode.NEW : {
int i = ByteArray.readU16bit(code, pos + 1);
stackTypes[stackTop++]
@@ -643,17 +615,21 @@ public abstract class Tracer implements TypeTag {
= new TypeData.ClassName(type);
return 3; }
case Opcode.ARRAYLENGTH :
- TypeData.setType(stackTypes[stackTop - 1], "[Ljava.lang.Object;", classPool);
+ stackTypes[stackTop - 1].setType("[Ljava.lang.Object;", classPool);
stackTypes[stackTop - 1] = INTEGER;
break;
case Opcode.ATHROW :
- TypeData.setType(stackTypes[--stackTop], "java.lang.Throwable", classPool);
+ stackTypes[--stackTop].setType("java.lang.Throwable", classPool);
visitThrow(pos, code);
break;
case Opcode.CHECKCAST : {
// TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool);
int i = ByteArray.readU16bit(code, pos + 1);
- stackTypes[stackTop - 1] = new TypeData.ClassName(cpool.getClassInfo(i));
+ String type = cpool.getClassInfo(i);
+ if (type.charAt(0) == '[')
+ type = type.replace('.', '/'); // getClassInfo() may return "[java.lang.Object;".
+
+ stackTypes[stackTop - 1] = new TypeData.ClassName(type);
return 3; }
case Opcode.INSTANCEOF :
// TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool);
@@ -747,9 +723,9 @@ public abstract class Tracer implements TypeTag {
stackTop -= Descriptor.dataSize(desc);
char c = desc.charAt(0);
if (c == 'L')
- TypeData.setType(stackTypes[stackTop], getFieldClassName(desc, 0), classPool);
+ stackTypes[stackTop].setType(getFieldClassName(desc, 0), classPool);
else if (c == '[')
- TypeData.setType(stackTypes[stackTop], desc, classPool);
+ stackTypes[stackTop].setType(desc, classPool);
setFieldTarget(notStatic, index);
return 3;
@@ -766,7 +742,7 @@ public abstract class Tracer implements TypeTag {
private void setFieldTarget(boolean notStatic, int index) throws BadBytecode {
if (notStatic) {
String className = cpool.getFieldrefClassName(index);
- TypeData.setType(stackTypes[--stackTop], className, classPool);
+ stackTypes[--stackTop].setType(className, classPool);
}
}
@@ -822,19 +798,54 @@ public abstract class Tracer implements TypeTag {
checkParamTypes(desc, 1);
if (notStatic) {
String className = cpool.getMethodrefClassName(i);
- TypeData.setType(stackTypes[--stackTop], className, classPool);
+ TypeData target = stackTypes[--stackTop];
+ if (target instanceof TypeData.UninitTypeVar && target.isUninit())
+ constructorCalled(target, ((TypeData.UninitTypeVar)target).offset());
+ else if (target instanceof TypeData.UninitData)
+ constructorCalled(target, ((TypeData.UninitData)target).offset());
+
+ target.setType(className, classPool);
}
pushMemberType(desc);
return 3;
}
+ /* This is a constructor call on an uninitialized object.
+ * Sets flags of other references to that object.
+ *
+ * @param offset the offset where the object has been created.
+ */
+ private void constructorCalled(TypeData target, int offset) {
+ target.constructorCalled(offset);
+ for (int i = 0; i < stackTop; i++)
+ stackTypes[i].constructorCalled(offset);
+
+ for (int i = 0; i < localsTypes.length; i++)
+ localsTypes[i].constructorCalled(offset);
+ }
+
private int doInvokeIntfMethod(int pos, byte[] code) throws BadBytecode {
int i = ByteArray.readU16bit(code, pos + 1);
String desc = cpool.getInterfaceMethodrefType(i);
checkParamTypes(desc, 1);
String className = cpool.getInterfaceMethodrefClassName(i);
- TypeData.setType(stackTypes[--stackTop], className, classPool);
+ stackTypes[--stackTop].setType(className, classPool);
+ pushMemberType(desc);
+ return 5;
+ }
+
+ private int doInvokeDynamic(int pos, byte[] code) throws BadBytecode {
+ int i = ByteArray.readU16bit(code, pos + 1);
+ String desc = cpool.getInvokeDynamicType(i);
+ checkParamTypes(desc, 1);
+
+ // assume CosntPool#REF_invokeStatic
+ /* TypeData target = stackTypes[--stackTop];
+ if (target instanceof TypeData.UninitTypeVar && target.isUninit())
+ constructorCalled((TypeData.UninitTypeVar)target);
+ */
+
pushMemberType(desc);
return 5;
}
@@ -911,10 +922,9 @@ public abstract class Tracer implements TypeTag {
stackTop--;
if (array)
- TypeData.setType(stackTypes[stackTop],
- desc.substring(i, k), classPool);
+ stackTypes[stackTop].setType(desc.substring(i, k), classPool);
else if (c == 'L')
- TypeData.setType(stackTypes[stackTop],
- desc.substring(i + 1, k - 1).replace('/', '.'), classPool);
+ stackTypes[stackTop].setType(desc.substring(i + 1, k - 1).replace('/', '.'),
+ classPool);
}
}
diff --git a/src/main/javassist/bytecode/stackmap/TypeData.java b/src/main/javassist/bytecode/stackmap/TypeData.java
index f6c6c4e..9bc837d 100644
--- a/src/main/javassist/bytecode/stackmap/TypeData.java
+++ b/src/main/javassist/bytecode/stackmap/TypeData.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,22 +16,34 @@
package javassist.bytecode.stackmap;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
import javassist.bytecode.StackMapTable;
-import javassist.bytecode.BadBytecode;
-import java.util.ArrayList;
public abstract class TypeData {
/* Memo:
* array type is a subtype of Cloneable and Serializable
*/
- protected TypeData() {}
+ public static TypeData[] make(int size) {
+ TypeData[] array = new TypeData[size];
+ for (int i = 0; i < size; i++)
+ array[i] = TypeTag.TOP;
+
+ return array;
+ }
- public abstract void merge(TypeData neighbor);
+ protected TypeData() {}
/**
* Sets the type name of this object type. If the given type name is
@@ -39,36 +52,71 @@ public abstract class TypeData {
*
* @param className dot-separated name unless the type is an array type.
*/
- static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode {
- if (td == TypeTag.TOP)
- throw new BadBytecode("unset variable");
- else
- td.setType(className, cp);
+ @SuppressWarnings("unused")
+ private static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode {
+ td.setType(className, cp);
}
- public abstract boolean equals(Object obj);
-
public abstract int getTypeTag();
public abstract int getTypeData(ConstPool cp);
- /*
- * See UninitData.getSelf().
- */
- public TypeData getSelf() { return this; }
+ public TypeData join() { return new TypeVar(this); }
- /* An operand value is copied when it is stored in a
- * local variable.
+ /**
+ * If the type is a basic type, this method normalizes the type
+ * and returns a BasicType object. Otherwise, it returns null.
*/
- public abstract TypeData copy();
+ public abstract BasicType isBasicType();
+
+ public abstract boolean is2WordType();
- public abstract boolean isObjectType();
- public boolean is2WordType() { return false; }
+ /**
+ * Returns false if getName() returns a valid type name.
+ */
public boolean isNullType() { return false; }
- public abstract String getName() throws BadBytecode;
- protected abstract void setType(String s, ClassPool cp) throws BadBytecode;
- public abstract void evalExpectedType(ClassPool cp) throws BadBytecode;
- public abstract String getExpected() throws BadBytecode;
+ public boolean isUninit() { return false; }
+
+ public abstract boolean eq(TypeData d);
+
+ public abstract String getName();
+ public abstract void setType(String s, ClassPool cp) throws BadBytecode;
+
+ /**
+ * @param dim array dimension. It may be negative.
+ */
+ public abstract TypeData getArrayType(int dim) throws NotFoundException;
+
+ /**
+ * Depth-first search by Tarjan's algorithm
+ *
+ * @param order a node stack in the order in which nodes are visited.
+ * @param index the index used by the algorithm.
+ */
+ public int dfs(List<TypeData> order, int index, ClassPool cp)
+ throws NotFoundException
+ {
+ return index;
+ }
+
+ /**
+ * Returns this if it is a TypeVar or a TypeVar that this
+ * type depends on. Otherwise, this method returns null.
+ * It is used by dfs().
+ *
+ * @param dim dimension
+ */
+ protected TypeVar toTypeVar(int dim) { return null; }
+
+ // see UninitTypeVar and UninitData
+ public void constructorCalled(int offset) {}
+
+ @Override
+ public String toString() {
+ return super.toString() + "(" + toString2(new HashSet<TypeData>()) + ")";
+ }
+
+ abstract String toString2(Set<TypeData> set);
/**
* Primitive types.
@@ -76,420 +124,860 @@ public abstract class TypeData {
protected static class BasicType extends TypeData {
private String name;
private int typeTag;
+ private char decodedName;
- public BasicType(String type, int tag) {
+ public BasicType(String type, int tag, char decoded) {
name = type;
typeTag = tag;
+ decodedName = decoded;
}
- public void merge(TypeData neighbor) {}
-
- public boolean equals(Object obj) {
- return this == obj;
- }
-
+ @Override
public int getTypeTag() { return typeTag; }
+ @Override
public int getTypeData(ConstPool cp) { return 0; }
- public boolean isObjectType() { return false; }
+ @Override
+ public TypeData join() {
+ if (this == TypeTag.TOP)
+ return this;
+ return super.join();
+ }
+ @Override
+ public BasicType isBasicType() { return this; }
+
+ @Override
public boolean is2WordType() {
return typeTag == StackMapTable.LONG
|| typeTag == StackMapTable.DOUBLE;
}
- public TypeData copy() {
- return this;
- }
-
- public void evalExpectedType(ClassPool cp) throws BadBytecode {}
-
- public String getExpected() throws BadBytecode {
- return name;
- }
+ @Override
+ public boolean eq(TypeData d) { return this == d; }
+ @Override
public String getName() {
return name;
}
- protected void setType(String s, ClassPool cp) throws BadBytecode {
+ public char getDecodedName() { return decodedName; }
+
+ @Override
+ public void setType(String s, ClassPool cp) throws BadBytecode {
throw new BadBytecode("conflict: " + name + " and " + s);
}
- public String toString() { return name; }
- }
+ /**
+ * @param dim array dimension. It may be negative.
+ */
+ @Override
+ public TypeData getArrayType(int dim) throws NotFoundException {
+ if (this == TypeTag.TOP)
+ return this;
+ else if (dim < 0)
+ throw new NotFoundException("no element type: " + name);
+ else if (dim == 0)
+ return this;
+ else {
+ char[] name = new char[dim + 1];
+ for (int i = 0; i < dim; i++)
+ name[i] = '[';
- protected static abstract class TypeName extends TypeData {
- protected ArrayList equivalences;
+ name[dim] = decodedName;
+ return new ClassName(new String(name));
+ }
+ }
- protected String expectedName;
- private CtClass cache;
- private boolean evalDone;
+ @Override
+ String toString2(Set<TypeData> set) { return name; }
+ }
+
+ // a type variable
+ public static abstract class AbsTypeVar extends TypeData {
+ public AbsTypeVar() {}
+ public abstract void merge(TypeData t);
+ @Override
+ public int getTypeTag() { return StackMapTable.OBJECT; }
- protected TypeName() {
- equivalences = new ArrayList();
- equivalences.add(this);
- expectedName = null;
- cache = null;
- evalDone = false;
+ @Override
+ public int getTypeData(ConstPool cp) {
+ return cp.addClassInfo(getName());
}
- public void merge(TypeData neighbor) {
- if (this == neighbor)
- return;
+ @Override
+ public boolean eq(TypeData d) { return getName().equals(d.getName()); }
+ }
- if (!(neighbor instanceof TypeName))
- return; // neighbor might be UninitData
+ /* a type variable representing a class type or a basic type.
+ */
+ public static class TypeVar extends AbsTypeVar {
+ protected List<TypeData> lowers;// lower bounds of this type. ArrayList<TypeData>
+ protected List<TypeData> usedBy;// reverse relations of lowers
+ protected List<String> uppers; // upper bounds of this type.
+ protected String fixedType;
+ private boolean is2WordType; // cache
+
+ public TypeVar(TypeData t) {
+ uppers = null;
+ lowers = new ArrayList<TypeData>(2);
+ usedBy = new ArrayList<TypeData>(2);
+ merge(t);
+ fixedType = null;
+ is2WordType = t.is2WordType();
+ }
+
+ @Override
+ public String getName() {
+ if (fixedType == null)
+ return lowers.get(0).getName();
+ return fixedType;
+ }
- TypeName neighbor2 = (TypeName)neighbor;
- ArrayList list = equivalences;
- ArrayList list2 = neighbor2.equivalences;
- if (list == list2)
- return;
+ @Override
+ public BasicType isBasicType() {
+ if (fixedType == null)
+ return lowers.get(0).isBasicType();
+ return null;
+ }
- int n = list2.size();
- for (int i = 0; i < n; i++) {
- TypeName tn = (TypeName)list2.get(i);
- add(list, tn);
- tn.equivalences = list;
+ @Override
+ public boolean is2WordType() {
+ if (fixedType == null) {
+ return is2WordType;
+ // return ((TypeData)lowers.get(0)).is2WordType();
}
+ return false;
}
- private static void add(ArrayList list, TypeData td) {
- int n = list.size();
- for (int i = 0; i < n; i++)
- if (list.get(i) == td)
- return;
+ @Override
+ public boolean isNullType() {
+ if (fixedType == null)
+ return lowers.get(0).isNullType();
+ return false;
+ }
- list.add(td);
+ @Override
+ public boolean isUninit() {
+ if (fixedType == null)
+ return lowers.get(0).isUninit();
+ return false;
}
- /* NullType overrides this method.
- */
- public int getTypeTag() { return StackMapTable.OBJECT; }
+ @Override
+ public void merge(TypeData t) {
+ lowers.add(t);
+ if (t instanceof TypeVar)
+ ((TypeVar)t).usedBy.add(this);
+ }
+ @Override
+ public int getTypeTag() {
+ /* If fixedType is null after calling dfs(), then this
+ type is NULL, Uninit, or a basic type. So call
+ getTypeTag() on the first element of lowers. */
+ if (fixedType == null)
+ return lowers.get(0).getTypeTag();
+ return super.getTypeTag();
+ }
+
+ @Override
public int getTypeData(ConstPool cp) {
- String type;
- try {
- type = getExpected();
- } catch (BadBytecode e) {
- throw new RuntimeException("fatal error: ", e);
- }
+ if (fixedType == null)
+ return lowers.get(0).getTypeData(cp);
+ return super.getTypeData(cp);
+ }
+
+ @Override
+ public void setType(String typeName, ClassPool cp) throws BadBytecode {
+ if (uppers == null)
+ uppers = new ArrayList<String>();
+
+ uppers.add(typeName);
+ }
+
+ private int visited = 0;
+ private int smallest = 0;
+ private boolean inList = false;
+ private int dimension = 0;
- return getTypeData2(cp, type);
+ @Override
+ protected TypeVar toTypeVar(int dim) {
+ dimension = dim;
+ return this;
}
- /* NullType overrides this method.
+ /* When fixTypes() is called, getName() will return the correct
+ * (i.e. fixed) type name.
*/
- protected int getTypeData2(ConstPool cp, String type) {
- return cp.addClassInfo(type);
+ @Override
+ public TypeData getArrayType(int dim) throws NotFoundException {
+ if (dim == 0)
+ return this;
+ BasicType bt = isBasicType();
+ if (bt == null)
+ if (isNullType())
+ return new NullType();
+ else
+ return new ClassName(getName()).getArrayType(dim);
+ return bt.getArrayType(dim);
}
- public boolean equals(Object obj) {
- if (obj instanceof TypeName) {
- try {
- TypeName tn = (TypeName)obj;
- return getExpected().equals(tn.getExpected());
+ // depth-first serach
+ @Override
+ public int dfs(List<TypeData> preOrder, int index, ClassPool cp) throws NotFoundException {
+ if (visited > 0)
+ return index; // MapMaker.make() may call an already visited node.
+
+ visited = smallest = ++index;
+ preOrder.add(this);
+ inList = true;
+ int n = lowers.size();
+ for (int i = 0; i < n; i++) {
+ TypeVar child = lowers.get(i).toTypeVar(dimension);
+ if (child != null)
+ if (child.visited == 0) {
+ index = child.dfs(preOrder, index, cp);
+ if (child.smallest < smallest)
+ smallest = child.smallest;
+ }
+ else if (child.inList)
+ if (child.visited < smallest)
+ smallest = child.visited;
+ }
+
+ if (visited == smallest) {
+ List<TypeData> scc = new ArrayList<TypeData>(); // strongly connected component
+ TypeVar cv;
+ do {
+ cv = (TypeVar)preOrder.remove(preOrder.size() - 1);
+ cv.inList = false;
+ scc.add(cv);
+ } while (cv != this);
+ fixTypes(scc, cp);
+ }
+
+ return index;
+ }
+
+ private void fixTypes(List<TypeData> scc, ClassPool cp) throws NotFoundException {
+ Set<String> lowersSet = new HashSet<String>();
+ boolean isBasicType = false;
+ TypeData kind = null;
+ int size = scc.size();
+ for (int i = 0; i < size; i++) {
+ TypeVar tvar = (TypeVar)scc.get(i);
+ List<TypeData> tds = tvar.lowers;
+ int size2 = tds.size();
+ for (int j = 0; j < size2; j++) {
+ TypeData td = tds.get(j);
+ TypeData d = td.getArrayType(tvar.dimension);
+ BasicType bt = d.isBasicType();
+ if (kind == null) {
+ if (bt == null) {
+ isBasicType = false;
+ kind = d;
+ /* If scc has only an UninitData, fixedType is kept null.
+ So lowerSet must be empty. If scc has not only an UninitData
+ but also another TypeData, an error must be thrown but this
+ error detection has not been implemented. */
+ if (d.isUninit())
+ break;
+ }
+ else {
+ isBasicType = true;
+ kind = bt;
+ }
+ }
+ else {
+ if ((bt == null && isBasicType) || (bt != null && kind != bt)) {
+ isBasicType = true;
+ kind = TypeTag.TOP;
+ break;
+ }
+ }
+
+ if (bt == null && !d.isNullType())
+ lowersSet.add(d.getName());
}
- catch (BadBytecode e) {}
}
- return false;
+ if (isBasicType) {
+ is2WordType = kind.is2WordType(); // necessary?
+ fixTypes1(scc, kind);
+ }
+ else {
+ String typeName = fixTypes2(scc, lowersSet, cp);
+ fixTypes1(scc, new ClassName(typeName));
+ }
}
- public boolean isObjectType() { return true; }
+ private void fixTypes1(List<TypeData> scc, TypeData kind) throws NotFoundException {
+ int size = scc.size();
+ for (int i = 0; i < size; i++) {
+ TypeVar cv = (TypeVar)scc.get(i);
+ TypeData kind2 = kind.getArrayType(-cv.dimension);
+ if (kind2.isBasicType() == null)
+ cv.fixedType = kind2.getName();
+ else {
+ cv.lowers.clear();
+ cv.lowers.add(kind2);
+ cv.is2WordType = kind2.is2WordType();
+ }
+ }
+ }
- protected void setType(String typeName, ClassPool cp) throws BadBytecode {
- if (update(cp, expectedName, typeName))
- expectedName = typeName;
+ private String fixTypes2(List<TypeData> scc, Set<String> lowersSet, ClassPool cp) throws NotFoundException {
+ Iterator<String> it = lowersSet.iterator();
+ if (lowersSet.size() == 0)
+ return null; // only NullType
+ else if (lowersSet.size() == 1)
+ return it.next();
+ else {
+ CtClass cc = cp.get(it.next());
+ while (it.hasNext())
+ cc = commonSuperClassEx(cc, cp.get(it.next()));
+
+ if (cc.getSuperclass() == null || isObjectArray(cc))
+ cc = fixByUppers(scc, cp, new HashSet<TypeData>(), cc);
+
+ if (cc.isArray())
+ return Descriptor.toJvmName(cc);
+
+ return cc.getName();
+ }
}
- public void evalExpectedType(ClassPool cp) throws BadBytecode {
- if (this.evalDone)
- return;
+ private static boolean isObjectArray(CtClass cc) throws NotFoundException {
+ return cc.isArray() && cc.getComponentType().getSuperclass() == null;
+ }
+
+ private CtClass fixByUppers(List<TypeData> users, ClassPool cp, Set<TypeData> visited, CtClass type)
+ throws NotFoundException
+ {
+ if (users == null)
+ return type;
+
+ int size = users.size();
+ for (int i = 0; i < size; i++) {
+ TypeVar t = (TypeVar)users.get(i);
+ if (!visited.add(t))
+ return type;
- ArrayList equiv = this.equivalences;
- int n = equiv.size();
- String name = evalExpectedType2(equiv, n);
- if (name == null) {
- name = this.expectedName;
- for (int i = 0; i < n; i++) {
- TypeData td = (TypeData)equiv.get(i);
- if (td instanceof TypeName) {
- TypeName tn = (TypeName)td;
- if (update(cp, name, tn.expectedName))
- name = tn.expectedName;
+ if (t.uppers != null) {
+ int s = t.uppers.size();
+ for (int k = 0; k < s; k++) {
+ CtClass cc = cp.get(t.uppers.get(k));
+ if (cc.subtypeOf(type))
+ type = cc;
}
}
+
+ type = fixByUppers(t.usedBy, cp, visited, type);
}
- for (int i = 0; i < n; i++) {
- TypeData td = (TypeData)equiv.get(i);
- if (td instanceof TypeName) {
- TypeName tn = (TypeName)td;
- tn.expectedName = name;
- tn.cache = null;
- tn.evalDone = true;
- }
+ return type;
+ }
+
+ @Override
+ String toString2(Set<TypeData> hash) {
+ hash.add(this);
+ if (lowers.size() > 0) {
+ TypeData e = lowers.get(0);
+ if (e != null && !hash.contains(e))
+ return e.toString2(hash);
}
+
+ return "?";
}
+ }
- private String evalExpectedType2(ArrayList equiv, int n) throws BadBytecode {
- String origName = null;
- for (int i = 0; i < n; i++) {
- TypeData td = (TypeData)equiv.get(i);
- if (!td.isNullType())
- if (origName == null)
- origName = td.getName();
- else if (!origName.equals(td.getName()))
- return null;
+ /**
+ * Finds the most specific common super class of the given classes
+ * by considering array types.
+ */
+ public static CtClass commonSuperClassEx(CtClass one, CtClass two) throws NotFoundException {
+ if (one == two)
+ return one;
+ else if (one.isArray() && two.isArray()) {
+ CtClass ele1 = one.getComponentType();
+ CtClass ele2 = two.getComponentType();
+ CtClass element = commonSuperClassEx(ele1, ele2);
+ if (element == ele1)
+ return one;
+ else if (element == ele2)
+ return two;
+ else
+ return one.getClassPool().get(element == null ? "java.lang.Object"
+ : element.getName() + "[]");
+ }
+ else if (one.isPrimitive() || two.isPrimitive())
+ return null; // TOP
+ else if (one.isArray() || two.isArray()) // but !(one.isArray() && two.isArray())
+ return one.getClassPool().get("java.lang.Object");
+ else
+ return commonSuperClass(one, two);
+ }
+
+ /**
+ * Finds the most specific common super class of the given classes.
+ * This method is a copy from javassist.bytecode.analysis.Type.
+ */
+ public static CtClass commonSuperClass(CtClass one, CtClass two) throws NotFoundException {
+ CtClass deep = one;
+ CtClass shallow = two;
+ CtClass backupShallow = shallow;
+ CtClass backupDeep = deep;
+
+ // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly
+ for (;;) {
+ // In case we get lucky, and find a match early
+ if (eq(deep, shallow) && deep.getSuperclass() != null)
+ return deep;
+
+ CtClass deepSuper = deep.getSuperclass();
+ CtClass shallowSuper = shallow.getSuperclass();
+
+ if (shallowSuper == null) {
+ // right, now reset shallow
+ shallow = backupShallow;
+ break;
}
- return origName;
- }
-
- protected boolean isTypeName() { return true; }
-
- private boolean update(ClassPool cp, String oldName, String typeName) throws BadBytecode {
- if (typeName == null)
- return false;
- else if (oldName == null)
- return true;
- else if (oldName.equals(typeName))
- return false;
- else if (typeName.charAt(0) == '['
- && oldName.equals("[Ljava.lang.Object;")) {
- /* this rule is not correct but Tracer class sets the type
- of the operand of arraylength to java.lang.Object[].
- Thus, int[] etc. must be a subtype of java.lang.Object[].
- */
- return true;
+ if (deepSuper == null) {
+ // wrong, swap them, since deep is now useless, its our tmp before we swap it
+ deep = backupDeep;
+ backupDeep = backupShallow;
+ backupShallow = deep;
+
+ deep = shallow;
+ shallow = backupShallow;
+ break;
}
- try {
- if (cache == null)
- cache = cp.get(oldName);
-
- CtClass cache2 = cp.get(typeName);
- if (cache2.subtypeOf(cache)) {
- cache = cache2;
- return true;
+ deep = deepSuper;
+ shallow = shallowSuper;
+ }
+
+ // Phase 2 - Move deepBackup up by (deep end - deep)
+ for (;;) {
+ deep = deep.getSuperclass();
+ if (deep == null)
+ break;
+
+ backupDeep = backupDeep.getSuperclass();
+ }
+
+ deep = backupDeep;
+
+ // Phase 3 - The hierarchy positions are now aligned
+ // The common super class is easy to find now
+ while (!eq(deep, shallow)) {
+ deep = deep.getSuperclass();
+ shallow = shallow.getSuperclass();
+ }
+
+ return deep;
+ }
+
+ static boolean eq(CtClass one, CtClass two) {
+ return one == two || (one != null && two != null && one.getName().equals(two.getName()));
+ }
+
+ public static void aastore(TypeData array, TypeData value, ClassPool cp) throws BadBytecode {
+ if (array instanceof AbsTypeVar)
+ if (!value.isNullType())
+ ((AbsTypeVar)array).merge(ArrayType.make(value));
+
+ if (value instanceof AbsTypeVar)
+ if (array instanceof AbsTypeVar)
+ ArrayElement.make(array); // should call value.setType() later.
+ else if (array instanceof ClassName) {
+ if (!array.isNullType()) {
+ String type = ArrayElement.typeName(array.getName());
+ value.setType(type, cp);
}
- else
- return false;
- }
- catch (NotFoundException e) {
- throw new BadBytecode("cannot find " + e.getMessage());
}
+ else
+ throw new BadBytecode("bad AASTORE: " + array);
+ }
+
+ /* A type variable representing an array type.
+ * It is a decorator of another type variable.
+ */
+ public static class ArrayType extends AbsTypeVar {
+ private AbsTypeVar element;
+
+ private ArrayType(AbsTypeVar elementType) {
+ element = elementType;
}
- /* See also NullType.getExpected().
- */
- public String getExpected() throws BadBytecode {
- ArrayList equiv = equivalences;
- if (equiv.size() == 1)
- return getName();
- else {
- String en = expectedName;
- if (en == null)
- return "java.lang.Object";
- else
- return en;
- }
+ static TypeData make(TypeData element) throws BadBytecode {
+ if (element instanceof ArrayElement)
+ return ((ArrayElement)element).arrayType();
+ else if (element instanceof AbsTypeVar)
+ return new ArrayType((AbsTypeVar)element);
+ else if (element instanceof ClassName)
+ if (!element.isNullType())
+ return new ClassName(typeName(element.getName()));
+
+ throw new BadBytecode("bad AASTORE: " + element);
}
- public String toString() {
+ @Override
+ public void merge(TypeData t) {
try {
- String en = expectedName;
- if (en != null)
- return en;
-
- String name = getName();
- if (equivalences.size() == 1)
- return name;
- else
- return name + "?";
+ if (!t.isNullType())
+ element.merge(ArrayElement.make(t));
}
catch (BadBytecode e) {
- return "<" + e.getMessage() + ">";
+ // never happens
+ throw new RuntimeException("fatal: " + e);
}
}
- }
- /**
- * Type data for OBJECT.
- */
- public static class ClassName extends TypeName {
- private String name; // dot separated. null if this object is a copy of another.
-
- public ClassName(String n) {
- name = n;
+ @Override
+ public String getName() {
+ return typeName(element.getName());
}
- public TypeData copy() {
- return new ClassName(name);
+ public AbsTypeVar elementType() { return element; }
+
+ @Override
+ public BasicType isBasicType() { return null; }
+ @Override
+ public boolean is2WordType() { return false; }
+
+ /* elementType must be a class name. Basic type names
+ * are not allowed.
+ */
+ public static String typeName(String elementType) {
+ if (elementType.charAt(0) == '[')
+ return "[" + elementType;
+ return "[L" + elementType.replace('.', '/') + ";";
}
- public String getName() { // never returns null.
- return name;
+ @Override
+ public void setType(String s, ClassPool cp) throws BadBytecode {
+ element.setType(ArrayElement.typeName(s), cp);
}
- }
- /**
- * Type data for NULL or OBJECT.
- * The types represented by the instances of this class are
- * initially NULL but will be OBJECT.
- */
- public static class NullType extends ClassName {
- public NullType() {
- super("null"); // type name
+ @Override
+ protected TypeVar toTypeVar(int dim) { return element.toTypeVar(dim + 1); }
+
+ @Override
+ public TypeData getArrayType(int dim) throws NotFoundException {
+ return element.getArrayType(dim + 1);
}
- public TypeData copy() {
- return new NullType();
+ @Override
+ public int dfs(List<TypeData> order, int index, ClassPool cp) throws NotFoundException {
+ return element.dfs(order, index, cp);
}
- public boolean isNullType() { return true; }
+ @Override
+ String toString2(Set<TypeData> set) {
+ return "[" + element.toString2(set);
+ }
+ }
- public int getTypeTag() {
+ /* A type variable representing an array-element type.
+ * It is a decorator of another type variable.
+ */
+ public static class ArrayElement extends AbsTypeVar {
+ private AbsTypeVar array;
+
+ private ArrayElement(AbsTypeVar a) { // a is never null
+ array = a;
+ }
+
+ public static TypeData make(TypeData array) throws BadBytecode {
+ if (array instanceof ArrayType)
+ return ((ArrayType)array).elementType();
+ else if (array instanceof AbsTypeVar)
+ return new ArrayElement((AbsTypeVar)array);
+ else if (array instanceof ClassName)
+ if (!array.isNullType())
+ return new ClassName(typeName(array.getName()));
+
+ throw new BadBytecode("bad AASTORE: " + array);
+ }
+
+ @Override
+ public void merge(TypeData t) {
try {
- if ("null".equals(getExpected()))
- return StackMapTable.NULL;
- else
- return super.getTypeTag();
+ if (!t.isNullType())
+ array.merge(ArrayType.make(t));
}
catch (BadBytecode e) {
- throw new RuntimeException("fatal error: ", e);
+ // never happens
+ throw new RuntimeException("fatal: " + e);
}
}
- protected int getTypeData2(ConstPool cp, String type) {
- if ("null".equals(type))
- return 0;
- else
- return super.getTypeData2(cp, type);
+ @Override
+ public String getName() {
+ return typeName(array.getName());
}
- public String getExpected() throws BadBytecode {
- String en = expectedName;
- if (en == null) {
- // ArrayList equiv = equivalences;
- // if (equiv.size() == 1)
- // return getName();
- // else
- return "java.lang.Object";
+ public AbsTypeVar arrayType() { return array; }
+
+ /* arrayType must be a class name. Basic type names are
+ * not allowed.
+ */
+
+ @Override
+ public BasicType isBasicType() { return null; }
+
+ @Override
+ public boolean is2WordType() { return false; }
+
+ private static String typeName(String arrayType) {
+ if (arrayType.length() > 1 && arrayType.charAt(0) == '[') {
+ char c = arrayType.charAt(1);
+ if (c == 'L')
+ return arrayType.substring(2, arrayType.length() - 1).replace('/', '.');
+ else if (c == '[')
+ return arrayType.substring(1);
}
- else
- return en;
+
+ return "java.lang.Object"; // the array type may be NullType
+ }
+
+ @Override
+ public void setType(String s, ClassPool cp) throws BadBytecode {
+ array.setType(ArrayType.typeName(s), cp);
+ }
+
+ @Override
+ protected TypeVar toTypeVar(int dim) { return array.toTypeVar(dim - 1); }
+
+ @Override
+ public TypeData getArrayType(int dim) throws NotFoundException {
+ return array.getArrayType(dim - 1);
+ }
+
+ @Override
+ public int dfs(List<TypeData> order, int index, ClassPool cp) throws NotFoundException {
+ return array.dfs(order, index, cp);
+ }
+
+ @Override
+ String toString2(Set<TypeData> set) {
+ return "*" + array.toString2(set);
+ }
+ }
+
+ public static class UninitTypeVar extends AbsTypeVar {
+ protected TypeData type; // UninitData or TOP
+
+ public UninitTypeVar(UninitData t) { type = t; }
+ @Override
+ public int getTypeTag() { return type.getTypeTag(); }
+ @Override
+ public int getTypeData(ConstPool cp) { return type.getTypeData(cp); }
+ @Override
+ public BasicType isBasicType() { return type.isBasicType(); }
+ @Override
+ public boolean is2WordType() { return type.is2WordType(); }
+ @Override
+ public boolean isUninit() { return type.isUninit(); }
+ @Override
+ public boolean eq(TypeData d) { return type.eq(d); }
+ @Override
+ public String getName() { return type.getName(); }
+
+ @Override
+ protected TypeVar toTypeVar(int dim) { return null; }
+ @Override
+ public TypeData join() { return type.join(); }
+
+ @Override
+ public void setType(String s, ClassPool cp) throws BadBytecode {
+ type.setType(s, cp);
+ }
+
+ @Override
+ public void merge(TypeData t) {
+ if (!t.eq(type))
+ type = TypeTag.TOP;
+ }
+
+ @Override
+ public void constructorCalled(int offset) {
+ type.constructorCalled(offset);
+ }
+
+ public int offset() {
+ if (type instanceof UninitData)
+ return ((UninitData)type).offset;
+ throw new RuntimeException("not available");
+ }
+
+ @Override
+ public TypeData getArrayType(int dim) throws NotFoundException {
+ return type.getArrayType(dim);
}
+
+ @Override
+ String toString2(Set<TypeData> set) { return ""; }
}
/**
- * Type data for OBJECT if the type is an object type and is
- * derived as an element type from an array type by AALOAD.
+ * Type data for OBJECT.
*/
- public static class ArrayElement extends TypeName {
- TypeData array;
-
- public ArrayElement(TypeData a) { // a is never null
- array = a;
+ public static class ClassName extends TypeData {
+ private String name; // dot separated.
+
+ public ClassName(String n) {
+ name = n;
}
- public TypeData copy() {
- return new ArrayElement(array);
+ @Override
+ public String getName() {
+ return name;
}
- protected void setType(String typeName, ClassPool cp) throws BadBytecode {
- super.setType(typeName, cp);
- array.setType(getArrayType(typeName), cp);
+ @Override
+ public BasicType isBasicType() { return null; }
+
+ @Override
+ public boolean is2WordType() { return false; }
+
+ @Override
+ public int getTypeTag() { return StackMapTable.OBJECT; }
+
+ @Override
+ public int getTypeData(ConstPool cp) {
+ return cp.addClassInfo(getName());
}
- public String getName() throws BadBytecode {
- String name = array.getName();
- if (name.length() > 1 && name.charAt(0) == '[') {
- char c = name.charAt(1);
- if (c == 'L')
- return name.substring(2, name.length() - 1).replace('/', '.');
- else if (c == '[')
- return name.substring(1);
+ @Override
+ public boolean eq(TypeData d) { return name.equals(d.getName()); }
+
+ @Override
+ public void setType(String typeName, ClassPool cp) throws BadBytecode {}
+
+ @Override
+ public TypeData getArrayType(int dim) throws NotFoundException {
+ if (dim == 0)
+ return this;
+ else if (dim > 0) {
+ char[] dimType = new char[dim];
+ for (int i = 0; i < dim; i++)
+ dimType[i] = '[';
+
+ String elementType = getName();
+ if (elementType.charAt(0) != '[')
+ elementType = "L" + elementType.replace('.', '/') + ";";
+
+ return new ClassName(new String(dimType) + elementType);
+ }
+ else {
+ for (int i = 0; i < -dim; i++)
+ if (name.charAt(i) != '[')
+ throw new NotFoundException("no " + dim + " dimensional array type: " + getName());
+
+ char type = name.charAt(-dim);
+ if (type == '[')
+ return new ClassName(name.substring(-dim));
+ else if (type == 'L')
+ return new ClassName(name.substring(-dim + 1, name.length() - 1).replace('/', '.'));
+ else if (type == TypeTag.DOUBLE.decodedName)
+ return TypeTag.DOUBLE;
+ else if (type == TypeTag.FLOAT.decodedName)
+ return TypeTag.FLOAT;
+ else if (type == TypeTag.LONG.decodedName)
+ return TypeTag.LONG;
+ else
+ return TypeTag.INTEGER;
}
-
- throw new BadBytecode("bad array type for AALOAD: "
- + name);
}
- public static String getArrayType(String elementType) {
- if (elementType.charAt(0) == '[')
- return "[" + elementType;
- else
- return "[L" + elementType.replace('.', '/') + ";";
+ @Override
+ String toString2(Set<TypeData> set) {
+ return name;
}
+ }
- public static String getElementType(String arrayType) {
- char c = arrayType.charAt(1);
- if (c == 'L')
- return arrayType.substring(2, arrayType.length() - 1).replace('/', '.');
- else if (c == '[')
- return arrayType.substring(1);
- else
- return arrayType;
+ /**
+ * Type data for NULL or OBJECT.
+ * The types represented by the instances of this class are
+ * initially NULL but will be OBJECT.
+ */
+ public static class NullType extends ClassName {
+ public NullType() {
+ super("null-type"); // type name
}
+
+ @Override
+ public int getTypeTag() {
+ return StackMapTable.NULL;
+ }
+
+ @Override
+ public boolean isNullType() { return true; }
+ @Override
+ public int getTypeData(ConstPool cp) { return 0; }
+
+ @Override
+ public TypeData getArrayType(int dim) { return this; }
}
/**
* Type data for UNINIT.
*/
- public static class UninitData extends TypeData {
- String className;
+ public static class UninitData extends ClassName {
int offset;
boolean initialized;
UninitData(int offset, String className) {
- this.className = className;
+ super(className);
this.offset = offset;
this.initialized = false;
}
- public void merge(TypeData neighbor) {}
-
- public int getTypeTag() { return StackMapTable.UNINIT; }
- public int getTypeData(ConstPool cp) { return offset; }
+ public UninitData copy() { return new UninitData(offset, getName()); }
- public boolean equals(Object obj) {
- if (obj instanceof UninitData) {
- UninitData ud = (UninitData)obj;
- return offset == ud.offset && className.equals(ud.className);
- }
- else
- return false;
+ @Override
+ public int getTypeTag() {
+ return StackMapTable.UNINIT;
}
- public TypeData getSelf() {
- if (initialized)
- return copy();
- else
- return this;
+ @Override
+ public int getTypeData(ConstPool cp) {
+ return offset;
}
- public TypeData copy() {
- return new ClassName(className);
+ @Override
+ public TypeData join() {
+ if (initialized)
+ return new TypeVar(new ClassName(getName()));
+ return new UninitTypeVar(copy());
}
- public boolean isObjectType() { return true; }
+ @Override
+ public boolean isUninit() { return true; }
- protected void setType(String typeName, ClassPool cp) throws BadBytecode {
- initialized = true;
+ @Override
+ public boolean eq(TypeData d) {
+ if (d instanceof UninitData) {
+ UninitData ud = (UninitData)d;
+ return offset == ud.offset && getName().equals(ud.getName());
+ }
+ return false;
}
- public void evalExpectedType(ClassPool cp) throws BadBytecode {}
+ public int offset() { return offset; }
- public String getName() {
- return className;
+ @Override
+ public void constructorCalled(int offset) {
+ if (offset == this.offset)
+ initialized = true;
}
- public String getExpected() { return className; }
-
- public String toString() { return "uninit:" + className + "@" + offset; }
+ @Override
+ String toString2(Set<TypeData> set) { return getName() + "," + offset; }
}
public static class UninitThis extends UninitData {
@@ -497,13 +985,20 @@ public abstract class TypeData {
super(-1, className);
}
- public int getTypeTag() { return StackMapTable.THIS; }
- public int getTypeData(ConstPool cp) { return 0; }
+ @Override
+ public UninitData copy() { return new UninitThis(getName()); }
- public boolean equals(Object obj) {
- return obj instanceof UninitThis;
+ @Override
+ public int getTypeTag() {
+ return StackMapTable.THIS;
+ }
+
+ @Override
+ public int getTypeData(ConstPool cp) {
+ return 0;
}
- public String toString() { return "uninit:this"; }
+ @Override
+ String toString2(Set<TypeData> set) { return "uninit:this"; }
}
}
diff --git a/src/main/javassist/bytecode/stackmap/TypeTag.java b/src/main/javassist/bytecode/stackmap/TypeTag.java
index 4172068..6fcdb24 100644
--- a/src/main/javassist/bytecode/stackmap/TypeTag.java
+++ b/src/main/javassist/bytecode/stackmap/TypeTag.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -18,11 +19,12 @@ package javassist.bytecode.stackmap;
import javassist.bytecode.StackMapTable;
public interface TypeTag {
- TypeData TOP = null;
- TypeData INTEGER = new TypeData.BasicType("int", StackMapTable.INTEGER);
- TypeData FLOAT = new TypeData.BasicType("float", StackMapTable.FLOAT);
- TypeData DOUBLE = new TypeData.BasicType("double", StackMapTable.DOUBLE);
- TypeData LONG = new TypeData.BasicType("long", StackMapTable.LONG);
+ String TOP_TYPE = "*top*";
+ TypeData.BasicType TOP = new TypeData.BasicType(TOP_TYPE, StackMapTable.TOP, ' ');
+ TypeData.BasicType INTEGER = new TypeData.BasicType("int", StackMapTable.INTEGER, 'I');
+ TypeData.BasicType FLOAT = new TypeData.BasicType("float", StackMapTable.FLOAT, 'F');
+ TypeData.BasicType DOUBLE = new TypeData.BasicType("double", StackMapTable.DOUBLE, 'D');
+ TypeData.BasicType LONG = new TypeData.BasicType("long", StackMapTable.LONG, 'J');
// and NULL, THIS, OBJECT, UNINIT
}
diff --git a/src/main/javassist/bytecode/stackmap/TypedBlock.java b/src/main/javassist/bytecode/stackmap/TypedBlock.java
index 65dce97..14fa7f2 100644
--- a/src/main/javassist/bytecode/stackmap/TypedBlock.java
+++ b/src/main/javassist/bytecode/stackmap/TypedBlock.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,26 +16,24 @@
package javassist.bytecode.stackmap;
-import javassist.bytecode.*;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.MethodInfo;
public class TypedBlock extends BasicBlock {
public int stackTop, numLocals;
- public TypeData[] stackTypes, localsTypes;
-
- // set by a Liveness object.
- // inputs[i] is true if the i-th variable is used within this block.
- public boolean[] inputs;
-
- // working area for Liveness class.
- public boolean updating;
- public int status;
- public byte[] localsUsage;
+ // localsTypes is set to non-null when this block is first visited by a MapMaker.
+ // see alreadySet().
+ public TypeData[] localsTypes;
+ public TypeData[] stackTypes;
/**
* Divides the method body into basic blocks.
* The type information of the first block is initialized.
*
- * @param optmize if it is true and the method does not include
+ * @param optimize if it is true and the method does not include
* branches, this method returns null.
*/
public static TypedBlock[] makeBlocks(MethodInfo minfo, CodeAttribute ca,
@@ -51,29 +50,21 @@ public class TypedBlock extends BasicBlock {
blocks[0].initFirstBlock(ca.getMaxStack(), ca.getMaxLocals(),
pool.getClassName(), minfo.getDescriptor(),
isStatic, minfo.isConstructor());
- new Liveness().compute(ca.iterator(), blocks, ca.getMaxLocals(),
- blocks[0].localsTypes);
return blocks;
}
protected TypedBlock(int pos) {
super(pos);
localsTypes = null;
- inputs = null;
- updating = false;
}
+ @Override
protected void toString2(StringBuffer sbuf) {
super.toString2(sbuf);
sbuf.append(",\n stack={");
printTypes(sbuf, stackTop, stackTypes);
sbuf.append("}, locals={");
printTypes(sbuf, numLocals, localsTypes);
- sbuf.append("}, inputs={");
- if (inputs != null)
- for (int i = 0; i < inputs.length; i++)
- sbuf.append(inputs[i] ? "1, " : "0, ");
-
sbuf.append('}');
}
@@ -110,10 +101,9 @@ public class TypedBlock extends BasicBlock {
public void resetNumLocals() {
if (localsTypes != null) {
int nl = localsTypes.length;
- while (nl > 0 && localsTypes[nl - 1] == TypeTag.TOP) {
+ while (nl > 0 && localsTypes[nl - 1].isBasicType() == TypeTag.TOP) {
if (nl > 1) {
- TypeData td = localsTypes[nl - 2];
- if (td == TypeTag.LONG || td == TypeTag.DOUBLE)
+ if (localsTypes[nl - 2].is2WordType())
break;
}
@@ -125,10 +115,12 @@ public class TypedBlock extends BasicBlock {
}
public static class Maker extends BasicBlock.Maker {
+ @Override
protected BasicBlock makeBlock(int pos) {
return new TypedBlock(pos);
}
+ @Override
protected BasicBlock[] makeArray(int size) {
return new TypedBlock[size];
}
@@ -152,8 +144,8 @@ public class TypedBlock extends BasicBlock {
throw new BadBytecode("no method descriptor: " + methodDesc);
stackTop = 0;
- stackTypes = new TypeData[maxStack];
- TypeData[] locals = new TypeData[maxLocals];
+ stackTypes = TypeData.make(maxStack);
+ TypeData[] locals = TypeData.make(maxLocals);
if (isConstructor)
locals[0] = new TypeData.UninitThis(className);
else if (!isStatic)
diff --git a/src/main/javassist/compiler/AccessorMaker.java b/src/main/javassist/compiler/AccessorMaker.java
index 7f4f918..02030c1 100644
--- a/src/main/javassist/compiler/AccessorMaker.java
+++ b/src/main/javassist/compiler/AccessorMaker.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,22 @@
package javassist.compiler;
-import javassist.*;
-import javassist.bytecode.*;
import java.util.HashMap;
+import java.util.Map;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.NotFoundException;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.ExceptionsAttribute;
+import javassist.bytecode.FieldInfo;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.SyntheticAttribute;
/**
* AccessorMaker maintains accessors to private members of an enclosing
@@ -26,14 +40,14 @@ import java.util.HashMap;
public class AccessorMaker {
private CtClass clazz;
private int uniqueNumber;
- private HashMap accessors;
+ private Map<String,Object> accessors;
static final String lastParamType = "javassist.runtime.Inner";
public AccessorMaker(CtClass c) {
clazz = c;
uniqueNumber = 1;
- accessors = new HashMap();
+ accessors = new HashMap<String,Object>();
}
public String getConstructor(CtClass c, String desc, MethodInfo orig)
@@ -89,7 +103,7 @@ public class AccessorMaker {
* @param accDesc the descriptor of the accessor method. The first
* parameter type is <code>clazz</code>.
* If the private method is static,
- * <code>accDesc<code> must be identical to <code>desc</code>.
+ * <code>accDesc</code> must be identical to <code>desc</code>.
*
* @param orig the method info of the private method.
* @return
diff --git a/src/main/javassist/compiler/CodeGen.java b/src/main/javassist/compiler/CodeGen.java
index 470a976..d4c748f 100644
--- a/src/main/javassist/compiler/CodeGen.java
+++ b/src/main/javassist/compiler/CodeGen.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -17,10 +18,36 @@ package javassist.compiler;
import java.util.ArrayList;
import java.util.Arrays;
-import javassist.compiler.ast.*;
-import javassist.bytecode.*;
-
-/* The code generator is implemeted by three files:
+import java.util.List;
+
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.Opcode;
+import javassist.compiler.ast.ASTList;
+import javassist.compiler.ast.ASTree;
+import javassist.compiler.ast.ArrayInit;
+import javassist.compiler.ast.AssignExpr;
+import javassist.compiler.ast.BinExpr;
+import javassist.compiler.ast.CallExpr;
+import javassist.compiler.ast.CastExpr;
+import javassist.compiler.ast.CondExpr;
+import javassist.compiler.ast.Declarator;
+import javassist.compiler.ast.DoubleConst;
+import javassist.compiler.ast.Expr;
+import javassist.compiler.ast.FieldDecl;
+import javassist.compiler.ast.InstanceOfExpr;
+import javassist.compiler.ast.IntConst;
+import javassist.compiler.ast.Keyword;
+import javassist.compiler.ast.Member;
+import javassist.compiler.ast.MethodDecl;
+import javassist.compiler.ast.NewExpr;
+import javassist.compiler.ast.Pair;
+import javassist.compiler.ast.Stmnt;
+import javassist.compiler.ast.StringL;
+import javassist.compiler.ast.Symbol;
+import javassist.compiler.ast.Variable;
+import javassist.compiler.ast.Visitor;
+
+/* The code generator is implemented by three files:
* CodeGen.java, MemberCodeGen.java, and JvstCodeGen.
* I just wanted to split a big file into three smaller ones.
*/
@@ -46,7 +73,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
*/
public boolean inStaticMethod;
- protected ArrayList breakList, continueList;
+ protected List<Integer> breakList, continueList;
/**
* doit() in ReturnHook is called from atReturn().
@@ -170,18 +197,16 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
if (dim == 0)
return name;
- else {
- StringBuffer sbuf = new StringBuffer();
- int d = dim;
- while (d-- > 0)
- sbuf.append('[');
+ StringBuffer sbuf = new StringBuffer();
+ int d = dim;
+ while (d-- > 0)
+ sbuf.append('[');
- sbuf.append('L');
- sbuf.append(name);
- sbuf.append(';');
+ sbuf.append('L');
+ sbuf.append(name);
+ sbuf.append(';');
- return sbuf.toString();
- }
+ return sbuf.toString();
}
protected static String toJvmTypeName(int type, int dim) {
@@ -241,16 +266,21 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
expr.accept(typeChecker);
}
+ @Override
public void atASTList(ASTList n) throws CompileError { fatal(); }
-
+
+ @Override
public void atPair(Pair n) throws CompileError { fatal(); }
+ @Override
public void atSymbol(Symbol n) throws CompileError { fatal(); }
+ @Override
public void atFieldDecl(FieldDecl field) throws CompileError {
field.getInit().accept(this);
}
+ @Override
public void atMethodDecl(MethodDecl method) throws CompileError {
ASTList mods = method.getModifiers();
setMaxLocals(1);
@@ -262,7 +292,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
inStaticMethod = true;
}
}
-
+
ASTList params = method.getParams();
while (params != null) {
atDeclarator((Declarator)params.head());
@@ -319,6 +349,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
protected abstract void insertDefaultSuperCall() throws CompileError;
+ @Override
public void atStmnt(Stmnt st) throws CompileError {
if (st == null)
return; // empty
@@ -380,7 +411,14 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
ASTree expr = st.head();
Stmnt thenp = (Stmnt)st.tail().head();
Stmnt elsep = (Stmnt)st.tail().tail().head();
- compileBooleanExpr(false, expr);
+ if (compileBooleanExpr(false, expr)) {
+ hasReturned = false;
+ if (elsep != null)
+ elsep.accept(this);
+
+ return;
+ }
+
int pc = bytecode.currentPc();
int pc2 = 0;
bytecode.addIndex(0); // correct later
@@ -399,7 +437,6 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
}
bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
-
if (elsep != null) {
elsep.accept(this);
if (!thenHasReturned)
@@ -410,10 +447,10 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
}
private void atWhileStmnt(Stmnt st, boolean notDo) throws CompileError {
- ArrayList prevBreakList = breakList;
- ArrayList prevContList = continueList;
- breakList = new ArrayList();
- continueList = new ArrayList();
+ List<Integer> prevBreakList = breakList;
+ List<Integer> prevContList = continueList;
+ breakList = new ArrayList<Integer>();
+ continueList = new ArrayList<Integer>();
ASTree expr = st.head();
Stmnt body = (Stmnt)st.tail();
@@ -434,8 +471,12 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
bytecode.write16bit(pc, pc3 - pc + 1);
boolean alwaysBranch = compileBooleanExpr(true, expr);
- bytecode.addIndex(pc2 - bytecode.currentPc() + 1);
+ if (alwaysBranch) {
+ bytecode.addOpcode(Opcode.GOTO);
+ alwaysBranch = breakList.size() == 0;
+ }
+ bytecode.addIndex(pc2 - bytecode.currentPc() + 1);
patchGoto(breakList, bytecode.currentPc());
patchGoto(continueList, pc3);
continueList = prevContList;
@@ -443,19 +484,16 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
hasReturned = alwaysBranch;
}
- protected void patchGoto(ArrayList list, int targetPc) {
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- int pc = ((Integer)list.get(i)).intValue();
+ protected void patchGoto(List<Integer> list, int targetPc) {
+ for (int pc:list)
bytecode.write16bit(pc, targetPc - pc + 1);
- }
}
private void atForStmnt(Stmnt st) throws CompileError {
- ArrayList prevBreakList = breakList;
- ArrayList prevContList = continueList;
- breakList = new ArrayList();
- continueList = new ArrayList();
+ List<Integer> prevBreakList = breakList;
+ List<Integer> prevContList = continueList;
+ breakList = new ArrayList<Integer>();
+ continueList = new ArrayList<Integer>();
Stmnt init = (Stmnt)st.head();
ASTList p = st.tail();
@@ -470,7 +508,14 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
int pc = bytecode.currentPc();
int pc2 = 0;
if (expr != null) {
- compileBooleanExpr(false, expr);
+ if (compileBooleanExpr(false, expr)) {
+ // in case of "for (...; false; ...)"
+ continueList = prevContList;
+ breakList = prevBreakList;
+ hasReturned = false;
+ return;
+ }
+
pc2 = bytecode.currentPc();
bytecode.addIndex(0);
}
@@ -499,8 +544,8 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
private void atSwitchStmnt(Stmnt st) throws CompileError {
compileExpr(st.head());
- ArrayList prevBreakList = breakList;
- breakList = new ArrayList();
+ List<Integer> prevBreakList = breakList;
+ breakList = new ArrayList<Integer>();
int opcodePc = bytecode.currentPc();
bytecode.addOpcode(LOOKUPSWITCH);
int npads = 3 - (opcodePc & 3);
@@ -565,8 +610,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
expr = TypeChecker.stripPlusExpr(expr);
if (expr instanceof IntConst)
return (int)((IntConst)expr).get();
- else
- throw new CompileError("bad case label");
+ throw new CompileError("bad case label");
}
private void atBreakStmnt(Stmnt st, boolean notCont)
@@ -577,7 +621,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
"sorry, not support labeled break or continue");
bytecode.addOpcode(Opcode.GOTO);
- Integer pc = new Integer(bytecode.currentPc());
+ Integer pc = Integer.valueOf(bytecode.currentPc());
bytecode.addIndex(0);
if (notCont)
breakList.add(pc);
@@ -654,6 +698,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
bc.addOpcode(MONITORENTER);
ReturnHook rh = new ReturnHook(this) {
+ @Override
protected boolean doit(Bytecode b, int opcode) {
b.addAload(var);
b.addOpcode(MONITOREXIT);
@@ -693,7 +738,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
"sorry, cannot break/continue in synchronized block");
}
- private static int getListSize(ArrayList list) {
+ private static int getListSize(List<Integer> list) {
return list == null ? 0 : list.size();
}
@@ -706,6 +751,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
return false;
}
+ @Override
public void atDeclarator(Declarator d) throws CompileError {
d.setLocalVar(getMaxLocals());
d.setClassName(resolveClassName(d.getClassName()));
@@ -727,10 +773,13 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
}
}
+ @Override
public abstract void atNewExpr(NewExpr n) throws CompileError;
+ @Override
public abstract void atArrayInit(ArrayInit init) throws CompileError;
+ @Override
public void atAssignExpr(AssignExpr expr) throws CompileError {
atAssignExpr(expr, true);
}
@@ -913,21 +962,25 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
return false;
}
+ @Override
public void atCondExpr(CondExpr expr) throws CompileError {
- booleanExpr(false, expr.condExpr());
- int pc = bytecode.currentPc();
- bytecode.addIndex(0); // correct later
- expr.thenExpr().accept(this);
- int dim1 = arrayDim;
- bytecode.addOpcode(Opcode.GOTO);
- int pc2 = bytecode.currentPc();
- bytecode.addIndex(0);
- bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
- expr.elseExpr().accept(this);
- if (dim1 != arrayDim)
- throw new CompileError("type mismatch in ?:");
+ if (booleanExpr(false, expr.condExpr()))
+ expr.elseExpr().accept(this);
+ else {
+ int pc = bytecode.currentPc();
+ bytecode.addIndex(0); // correct later
+ expr.thenExpr().accept(this);
+ int dim1 = arrayDim;
+ bytecode.addOpcode(Opcode.GOTO);
+ int pc2 = bytecode.currentPc();
+ bytecode.addIndex(0);
+ bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
+ expr.elseExpr().accept(this);
+ if (dim1 != arrayDim)
+ throw new CompileError("type mismatch in ?:");
- bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
+ bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
+ }
}
static final int[] binOp = {
@@ -953,6 +1006,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
return -1;
}
+ @Override
public void atBinExpr(BinExpr expr) throws CompileError {
int token = expr.getOperator();
@@ -981,11 +1035,13 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
else {
/* equation: &&, ||, ==, !=, <=, >=, <, >
*/
- booleanExpr(true, expr);
- bytecode.addIndex(7);
- bytecode.addIconst(0); // false
- bytecode.addOpcode(Opcode.GOTO);
- bytecode.addIndex(4);
+ if (!booleanExpr(true, expr)) {
+ bytecode.addIndex(7);
+ bytecode.addIconst(0); // false
+ bytecode.addOpcode(Opcode.GOTO);
+ bytecode.addIndex(4);
+ }
+
bytecode.addIconst(1); // true
}
}
@@ -1087,9 +1143,10 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
}
/* Produces the opcode to branch if the condition is true.
- * The oprand is not produced.
+ * The oprand (branch offset) is not produced.
*
* @return true if the compiled code is GOTO (always branch).
+ * GOTO is not produced.
*/
private boolean booleanExpr(boolean branchIf, ASTree expr)
throws CompileError
@@ -1104,22 +1161,29 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
compareExpr(branchIf, bexpr.getOperator(), type1, bexpr);
}
else if (op == '!')
- booleanExpr(!branchIf, ((Expr)expr).oprand1());
+ return booleanExpr(!branchIf, ((Expr)expr).oprand1());
else if ((isAndAnd = (op == ANDAND)) || op == OROR) {
BinExpr bexpr = (BinExpr)expr;
- booleanExpr(!isAndAnd, bexpr.oprand1());
- int pc = bytecode.currentPc();
- bytecode.addIndex(0); // correct later
-
- booleanExpr(isAndAnd, bexpr.oprand2());
- bytecode.write16bit(pc, bytecode.currentPc() - pc + 3);
- if (branchIf != isAndAnd) {
- bytecode.addIndex(6); // skip GOTO instruction
- bytecode.addOpcode(Opcode.GOTO);
+ if (booleanExpr(!isAndAnd, bexpr.oprand1())) {
+ exprType = BOOLEAN;
+ arrayDim = 0;
+ return true;
}
+ int pc = bytecode.currentPc();
+ bytecode.addIndex(0); // correct later
+ if (booleanExpr(isAndAnd, bexpr.oprand2()))
+ bytecode.addOpcode(Opcode.GOTO);
+
+ bytecode.write16bit(pc, bytecode.currentPc() - pc + 3);
+ if (branchIf != isAndAnd) {
+ bytecode.addIndex(6); // skip GOTO instruction
+ bytecode.addOpcode(Opcode.GOTO);
+ }
}
else if (isAlwaysBranch(expr, branchIf)) {
- bytecode.addOpcode(Opcode.GOTO);
+ // Opcode.GOTO is not added here. The caller must add it.
+ exprType = BOOLEAN;
+ arrayDim = 0;
return true; // always branch
}
else { // others
@@ -1135,7 +1199,6 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
return false;
}
-
private static boolean isAlwaysBranch(ASTree expr, boolean branchIf) {
if (expr instanceof Keyword) {
int t = ((Keyword)expr).get();
@@ -1175,8 +1238,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
if (type1 == NULL)
return exprType;
- else
- return type1;
+ return type1;
}
private static final int ifOp[] = { EQ, IF_ICMPEQ, IF_ICMPNE,
@@ -1358,6 +1420,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
bytecode.addOpcode(op);
}
+ @Override
public void atCastExpr(CastExpr expr) throws CompileError {
String cname = resolveClassName(expr.getClassName());
String toClass = checkCastExpr(expr, cname);
@@ -1371,6 +1434,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
bytecode.addCheckcast(toClass);
}
+ @Override
public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError {
String cname = resolveClassName(expr.getClassName());
String toClass = checkCastExpr(expr, cname);
@@ -1388,12 +1452,13 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
int type = expr.getType();
oprand.accept(this);
int srcType = exprType;
+ int srcDim = arrayDim;
if (invalidDim(srcType, arrayDim, className, type, dim, name, true)
|| srcType == VOID || type == VOID)
throw new CompileError(msg);
if (type == CLASS) {
- if (!isRefType(srcType))
+ if (!isRefType(srcType) && srcDim == 0)
throw new CompileError(msg);
return toJvmArrayName(name, dim);
@@ -1410,7 +1475,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
{
if (srcType == destType)
return;
-
+
int op, op2;
int stype = typePrecedence(srcType);
int dtype = typePrecedence(destType);
@@ -1442,6 +1507,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
bytecode.addOpcode(op2);
}
+ @Override
public void atExpr(Expr expr) throws CompileError {
// array access, member access,
// (unary) +, (unary) -, ++, --, !, ~
@@ -1450,7 +1516,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
ASTree oprand = expr.oprand1();
if (token == '.') {
String member = ((Symbol)expr.oprand2()).get();
- if (member.equals("class"))
+ if (member.equals("class"))
atClassObject(expr); // .class
else
atFieldRead(expr);
@@ -1467,11 +1533,13 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
else if (token == PLUSPLUS || token == MINUSMINUS)
atPlusPlus(token, oprand, expr, true);
else if (token == '!') {
- booleanExpr(false, expr);
- bytecode.addIndex(7);
- bytecode.addIconst(1);
- bytecode.addOpcode(Opcode.GOTO);
- bytecode.addIndex(4);
+ if (!booleanExpr(false, expr)) {
+ bytecode.addIndex(7);
+ bytecode.addIconst(1);
+ bytecode.addOpcode(Opcode.GOTO);
+ bytecode.addIndex(4);
+ }
+
bytecode.addIconst(0);
}
else if (token == CALL) // method call
@@ -1525,6 +1593,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
throw new CompileError("invalid type for " + expr.getName());
}
+ @Override
public abstract void atCallExpr(CallExpr expr) throws CompileError;
protected abstract void atFieldRead(ASTree expr) throws CompileError;
@@ -1714,7 +1783,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
if (doDup && isPost)
bytecode.addOpcode(DUP2);
- bytecode.addLconst((long)1);
+ bytecode.addLconst(1);
bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
if (doDup && !isPost)
bytecode.addOpcode(DUP2);
@@ -1800,7 +1869,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
exprType = INT;
}
else if (t == LONG) {
- bytecode.addLconst((long)1);
+ bytecode.addLconst(1);
bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
}
else if (t == FLOAT) {
@@ -1821,8 +1890,10 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
protected abstract void atFieldPlusPlus(int token, boolean isPost,
ASTree oprand, Expr expr, boolean doDup) throws CompileError;
+ @Override
public abstract void atMember(Member n) throws CompileError;
+ @Override
public void atVariable(Variable v) throws CompileError {
Declarator d = v.getDeclarator();
exprType = d.getType();
@@ -1852,6 +1923,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
}
}
+ @Override
public void atKeyword(Keyword k) throws CompileError {
arrayDim = 0;
int token = k.get();
@@ -1879,13 +1951,14 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
if (token == THIS)
className = getThisName();
else
- className = getSuperName();
+ className = getSuperName();
break;
default :
fatal();
}
}
+ @Override
public void atStringL(StringL s) throws CompileError {
exprType = CLASS;
arrayDim = 0;
@@ -1893,6 +1966,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
bytecode.addLdc(s.get());
}
+ @Override
public void atIntConst(IntConst i) throws CompileError {
arrayDim = 0;
long value = i.get();
@@ -1907,6 +1981,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
}
}
+ @Override
public void atDoubleConst(DoubleConst d) throws CompileError {
arrayDim = 0;
if (d.getType() == DoubleConstant) {
diff --git a/src/main/javassist/compiler/CompileError.java b/src/main/javassist/compiler/CompileError.java
index 2756d15..5b857cf 100644
--- a/src/main/javassist/compiler/CompileError.java
+++ b/src/main/javassist/compiler/CompileError.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -19,6 +20,8 @@ import javassist.CannotCompileException;
import javassist.NotFoundException;
public class CompileError extends Exception {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private Lex lex;
private String reason;
@@ -42,10 +45,12 @@ public class CompileError extends Exception {
public Lex getLex() { return lex; }
+ @Override
public String getMessage() {
return reason;
}
+ @Override
public String toString() {
return "compile error: " + reason;
}
diff --git a/src/main/javassist/compiler/Javac.java b/src/main/javassist/compiler/Javac.java
index 9314bbc..084487b 100644
--- a/src/main/javassist/compiler/Javac.java
+++ b/src/main/javassist/compiler/Javac.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,23 +16,31 @@
package javassist.compiler;
+import javassist.CannotCompileException;
+import javassist.CtBehavior;
import javassist.CtClass;
-import javassist.CtPrimitiveType;
-import javassist.CtMember;
+import javassist.CtConstructor;
import javassist.CtField;
-import javassist.CtBehavior;
+import javassist.CtMember;
import javassist.CtMethod;
-import javassist.CtConstructor;
-import javassist.CannotCompileException;
+import javassist.CtPrimitiveType;
import javassist.Modifier;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
import javassist.bytecode.Bytecode;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
-import javassist.bytecode.BadBytecode;
import javassist.bytecode.Opcode;
-import javassist.NotFoundException;
-
-import javassist.compiler.ast.*;
+import javassist.compiler.ast.ASTList;
+import javassist.compiler.ast.ASTree;
+import javassist.compiler.ast.CallExpr;
+import javassist.compiler.ast.Declarator;
+import javassist.compiler.ast.Expr;
+import javassist.compiler.ast.FieldDecl;
+import javassist.compiler.ast.Member;
+import javassist.compiler.ast.MethodDecl;
+import javassist.compiler.ast.Stmnt;
+import javassist.compiler.ast.Symbol;
public class Javac {
JvstCodeGen gen;
@@ -90,14 +99,12 @@ public class Javac {
try {
if (mem instanceof FieldDecl)
return compileField((FieldDecl)mem);
- else {
- CtBehavior cb = compileMethod(p, (MethodDecl)mem);
- CtClass decl = cb.getDeclaringClass();
- cb.getMethodInfo2()
- .rebuildStackMapIf6(decl.getClassPool(),
- decl.getClassFile2());
- return cb;
- }
+ CtBehavior cb = compileMethod(p, (MethodDecl)mem);
+ CtClass decl = cb.getDeclaringClass();
+ cb.getMethodInfo2()
+ .rebuildStackMapIf6(decl.getClassPool(),
+ decl.getClassFile2());
+ return cb;
}
catch (BadBytecode bb) {
throw new CompileError(bb.getMessage());
@@ -119,6 +126,7 @@ public class Javac {
protected void setInit(ASTree i) { init = i; }
+ @Override
protected ASTree getInitAST() {
return init;
}
@@ -157,24 +165,22 @@ public class Javac {
cons.setExceptionTypes(tlist);
return cons;
}
- else {
- Declarator r = md.getReturn();
- CtClass rtype = gen.resolver.lookupClass(r);
- recordReturnType(rtype, false);
- CtMethod method = new CtMethod(rtype, r.getVariable().get(),
- plist, gen.getThisClass());
- method.setModifiers(mod);
- gen.setThisMethod(method);
- md.accept(gen);
- if (md.getBody() != null)
- method.getMethodInfo().setCodeAttribute(
- bytecode.toCodeAttribute());
- else
- method.setModifiers(mod | Modifier.ABSTRACT);
+ Declarator r = md.getReturn();
+ CtClass rtype = gen.resolver.lookupClass(r);
+ recordReturnType(rtype, false);
+ CtMethod method = new CtMethod(rtype, r.getVariable().get(),
+ plist, gen.getThisClass());
+ method.setModifiers(mod);
+ gen.setThisMethod(method);
+ md.accept(gen);
+ if (md.getBody() != null)
+ method.getMethodInfo().setCodeAttribute(
+ bytecode.toCodeAttribute());
+ else
+ method.setModifiers(mod | Modifier.ABSTRACT);
- method.setExceptionTypes(tlist);
- return method;
- }
+ method.setExceptionTypes(tlist);
+ return method;
}
catch (NotFoundException e) {
throw new CompileError(e.toString());
@@ -184,8 +190,8 @@ public class Javac {
/**
* Compiles a method (or constructor) body.
*
- * @src a single statement or a block.
- * If null, this method produces a body returning zero or null.
+ * @param src a single statement or a block.
+ * If null, this method produces a body returning zero or null.
*/
public Bytecode compileBody(CtBehavior method, String src)
throws CompileError
@@ -345,7 +351,7 @@ public class Javac {
* <code>isStatic</code> must be recorded before compilation.
* <code>maxLocals</code> is updated to include $0,...
*
- * @paaram use0 true if $0 is used.
+ * @param use0 true if $0 is used.
* @param varNo the register number of $0 (use0 is true)
* or $1 (otherwise).
* @param target the type of $0 (it can be null if use0 is false).
@@ -436,6 +442,7 @@ public class Javac {
final String m = method;
ProceedHandler h = new ProceedHandler() {
+ @Override
public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
throws CompileError
{
@@ -448,6 +455,7 @@ public class Javac {
gen.addNullIfVoid();
}
+ @Override
public void setReturnType(JvstTypeChecker check, ASTList args)
throws CompileError
{
@@ -480,6 +488,7 @@ public class Javac {
final String m = method;
ProceedHandler h = new ProceedHandler() {
+ @Override
public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
throws CompileError
{
@@ -490,6 +499,7 @@ public class Javac {
gen.addNullIfVoid();
}
+ @Override
public void setReturnType(JvstTypeChecker check, ASTList args)
throws CompileError
{
@@ -515,27 +525,27 @@ public class Javac {
* @param methodname the method name.
* @param descriptor the method descriptor.
*/
- public void recordSpecialProceed(String target, String classname,
- String methodname, String descriptor)
+ public void recordSpecialProceed(String target, final String classname,
+ final String methodname, final String descriptor,
+ final int methodIndex)
throws CompileError
{
Parser p = new Parser(new Lex(target));
final ASTree texpr = p.parseExpression(stable);
- final String cname = classname;
- final String method = methodname;
- final String desc = descriptor;
ProceedHandler h = new ProceedHandler() {
+ @Override
public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
throws CompileError
{
- gen.compileInvokeSpecial(texpr, cname, method, desc, args);
+ gen.compileInvokeSpecial(texpr, methodIndex, descriptor, args);
}
+ @Override
public void setReturnType(JvstTypeChecker c, ASTList args)
throws CompileError
{
- c.compileInvokeSpecial(texpr, cname, method, desc, args);
+ c.compileInvokeSpecial(texpr, classname, methodname, descriptor, args);
}
};
diff --git a/src/main/javassist/compiler/JvstCodeGen.java b/src/main/javassist/compiler/JvstCodeGen.java
index 91b0eca..5a43d65 100644
--- a/src/main/javassist/compiler/JvstCodeGen.java
+++ b/src/main/javassist/compiler/JvstCodeGen.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,21 @@
package javassist.compiler;
-import javassist.*;
-import javassist.bytecode.*;
-import javassist.compiler.ast.*;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtPrimitiveType;
+import javassist.NotFoundException;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.Descriptor;
+import javassist.compiler.ast.ASTList;
+import javassist.compiler.ast.ASTree;
+import javassist.compiler.ast.CallExpr;
+import javassist.compiler.ast.CastExpr;
+import javassist.compiler.ast.Declarator;
+import javassist.compiler.ast.Expr;
+import javassist.compiler.ast.Member;
+import javassist.compiler.ast.Stmnt;
+import javassist.compiler.ast.Symbol;
/* Code generator accepting extended Java syntax for Javassist.
*/
@@ -35,6 +48,7 @@ public class JvstCodeGen extends MemberCodeGen {
private CtClass dollarType = null;
CtClass returnType = null;
String returnCastName = null;
+ @SuppressWarnings("unused")
private String returnVarName = null; // null if $_ is not used.
public static final String wrapperCastName = "$w";
String proceedName = null;
@@ -77,6 +91,7 @@ public class JvstCodeGen extends MemberCodeGen {
/* To support $args, $sig, and $type.
* $args is an array of parameter list.
*/
+ @Override
public void atMember(Member mem) throws CompileError {
String name = mem.get();
if (name.equals(paramArrayName)) {
@@ -119,6 +134,7 @@ public class JvstCodeGen extends MemberCodeGen {
className = "java/lang/Class";
}
+ @Override
protected void atFieldAssign(Expr expr, int op, ASTree left,
ASTree right, boolean doDup) throws CompileError
{
@@ -157,6 +173,7 @@ public class JvstCodeGen extends MemberCodeGen {
}
}
+ @Override
public void atCastExpr(CastExpr expr) throws CompileError {
ASTList classname = expr.getClassName();
if (classname != null && expr.getArrayDim() == 0) {
@@ -226,6 +243,7 @@ public class JvstCodeGen extends MemberCodeGen {
/* Delegates to a ProcHandler object if the method call is
* $proceed(). It may process $cflow().
*/
+ @Override
public void atCallExpr(CallExpr expr) throws CompileError {
ASTree method = expr.oprand1();
if (method instanceof Member) {
@@ -300,8 +318,7 @@ public class JvstCodeGen extends MemberCodeGen {
return (left instanceof Member
&& ((Member)left).get().equals(paramListName));
}
- else
- return false;
+ return false;
}
/*
@@ -313,6 +330,7 @@ public class JvstCodeGen extends MemberCodeGen {
}
*/
+ @Override
public int getMethodArgsLength(ASTList args) {
String pname = paramListName;
int n = 0;
@@ -331,6 +349,7 @@ public class JvstCodeGen extends MemberCodeGen {
return n;
}
+ @Override
public void atMethodArgs(ASTList args, int[] types, int[] dims,
String[] cnames) throws CompileError {
CtClass[] params = paramTypeList;
@@ -392,16 +411,15 @@ public class JvstCodeGen extends MemberCodeGen {
/* called by Javac#recordSpecialProceed().
*/
- void compileInvokeSpecial(ASTree target, String classname,
- String methodname, String descriptor,
- ASTList args)
+ void compileInvokeSpecial(ASTree target, int methodIndex,
+ String descriptor, ASTList args)
throws CompileError
{
target.accept(this);
int nargs = getMethodArgsLength(args);
atMethodArgs(args, new int[nargs], new int[nargs],
new String[nargs]);
- bytecode.addInvokespecial(classname, methodname, descriptor);
+ bytecode.addInvokespecial(methodIndex, descriptor);
setReturnType(descriptor, false, false);
addNullIfVoid();
}
@@ -409,6 +427,7 @@ public class JvstCodeGen extends MemberCodeGen {
/*
* Makes it valid to write "return <expr>;" for a void method.
*/
+ @Override
protected void atReturnStmnt(Stmnt st) throws CompileError {
ASTree result = st.getLeft();
if (result != null && returnType == CtClass.voidType) {
@@ -442,12 +461,10 @@ public class JvstCodeGen extends MemberCodeGen {
returnVarName = resultName;
if (resultName == null)
return -1;
- else {
- int varNo = getMaxLocals();
- int locals = varNo + recordVar(type, resultName, varNo, tbl);
- setMaxLocals(locals);
- return varNo;
- }
+ int varNo = getMaxLocals();
+ int locals = varNo + recordVar(type, resultName, varNo, tbl);
+ setMaxLocals(locals);
+ return varNo;
}
/**
@@ -537,12 +554,10 @@ public class JvstCodeGen extends MemberCodeGen {
{
if (varName == null)
return -1;
- else {
- int varNo = getMaxLocals();
- int locals = varNo + recordVar(type, varName, varNo, tbl);
- setMaxLocals(locals);
- return varNo;
- }
+ int varNo = getMaxLocals();
+ int locals = varNo + recordVar(type, varName, varNo, tbl);
+ setMaxLocals(locals);
+ return varNo;
}
private int recordVar(CtClass cc, String varName, int varNo,
@@ -608,36 +623,34 @@ public class JvstCodeGen extends MemberCodeGen {
code.addAnewarray(javaLangObject); // anewarray Object
return 1;
}
- else {
- CtClass[] args = new CtClass[1];
- int n = params.length;
- code.addIconst(n); // iconst_<n>
- code.addAnewarray(javaLangObject); // anewarray Object
- for (int i = 0; i < n; ++i) {
- code.addOpcode(Bytecode.DUP); // dup
- code.addIconst(i); // iconst_<i>
- if (params[i].isPrimitive()) {
- CtPrimitiveType pt = (CtPrimitiveType)params[i];
- String wrapper = pt.getWrapperName();
- code.addNew(wrapper); // new <wrapper>
- code.addOpcode(Bytecode.DUP); // dup
- int s = code.addLoad(regno, pt); // ?load <regno>
- regno += s;
- args[0] = pt;
- code.addInvokespecial(wrapper, "<init>",
- Descriptor.ofMethod(CtClass.voidType, args));
- // invokespecial
- }
- else {
- code.addAload(regno); // aload <regno>
- ++regno;
- }
-
- code.addOpcode(Bytecode.AASTORE); // aastore
+ CtClass[] args = new CtClass[1];
+ int n = params.length;
+ code.addIconst(n); // iconst_<n>
+ code.addAnewarray(javaLangObject); // anewarray Object
+ for (int i = 0; i < n; ++i) {
+ code.addOpcode(Bytecode.DUP); // dup
+ code.addIconst(i); // iconst_<i>
+ if (params[i].isPrimitive()) {
+ CtPrimitiveType pt = (CtPrimitiveType)params[i];
+ String wrapper = pt.getWrapperName();
+ code.addNew(wrapper); // new <wrapper>
+ code.addOpcode(Bytecode.DUP); // dup
+ int s = code.addLoad(regno, pt); // ?load <regno>
+ regno += s;
+ args[0] = pt;
+ code.addInvokespecial(wrapper, "<init>",
+ Descriptor.ofMethod(CtClass.voidType, args));
+ // invokespecial
+ }
+ else {
+ code.addAload(regno); // aload <regno>
+ ++regno;
}
- return 8;
+ code.addOpcode(Bytecode.AASTORE); // aastore
}
+
+ return 8;
}
protected void compileUnwrapValue(CtClass type, Bytecode code)
diff --git a/src/main/javassist/compiler/JvstTypeChecker.java b/src/main/javassist/compiler/JvstTypeChecker.java
index d88909e..13e4729 100644
--- a/src/main/javassist/compiler/JvstTypeChecker.java
+++ b/src/main/javassist/compiler/JvstTypeChecker.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,8 +16,17 @@
package javassist.compiler;
-import javassist.*;
-import javassist.compiler.ast.*;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtPrimitiveType;
+import javassist.NotFoundException;
+import javassist.compiler.ast.ASTList;
+import javassist.compiler.ast.ASTree;
+import javassist.compiler.ast.CallExpr;
+import javassist.compiler.ast.CastExpr;
+import javassist.compiler.ast.Expr;
+import javassist.compiler.ast.Member;
+import javassist.compiler.ast.Symbol;
/* Type checker accepting extended Java syntax for Javassist.
*/
@@ -43,6 +53,7 @@ public class JvstTypeChecker extends TypeChecker {
/* To support $args, $sig, and $type.
* $args is an array of parameter list.
*/
+ @Override
public void atMember(Member mem) throws CompileError {
String name = mem.get();
if (name.equals(codeGen.paramArrayName)) {
@@ -65,6 +76,7 @@ public class JvstTypeChecker extends TypeChecker {
super.atMember(mem);
}
+ @Override
protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right)
throws CompileError
{
@@ -83,6 +95,7 @@ public class JvstTypeChecker extends TypeChecker {
super.atFieldAssign(expr, op, left, right);
}
+ @Override
public void atCastExpr(CastExpr expr) throws CompileError {
ASTList classname = expr.getClassName();
if (classname != null && expr.getArrayDim() == 0) {
@@ -137,6 +150,7 @@ public class JvstTypeChecker extends TypeChecker {
/* Delegates to a ProcHandler object if the method call is
* $proceed(). It may process $cflow().
*/
+ @Override
public void atCallExpr(CallExpr expr) throws CompileError {
ASTree method = expr.oprand1();
if (method instanceof Member) {
@@ -174,10 +188,10 @@ public class JvstTypeChecker extends TypeChecker {
return (left instanceof Member
&& ((Member)left).get().equals(codeGen.paramListName));
}
- else
- return false;
+ return false;
}
+ @Override
public int getMethodArgsLength(ASTList args) {
String pname = codeGen.paramListName;
int n = 0;
@@ -196,6 +210,7 @@ public class JvstTypeChecker extends TypeChecker {
return n;
}
+ @Override
public void atMethodArgs(ASTList args, int[] types, int[] dims,
String[] cnames) throws CompileError {
CtClass[] params = codeGen.paramTypeList;
diff --git a/src/main/javassist/compiler/KeywordTable.java b/src/main/javassist/compiler/KeywordTable.java
index 3a22a79..65137d9 100644
--- a/src/main/javassist/compiler/KeywordTable.java
+++ b/src/main/javassist/compiler/KeywordTable.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,18 +16,19 @@
package javassist.compiler;
-public final class KeywordTable extends java.util.HashMap {
+import java.util.HashMap;
+
+public final class KeywordTable extends HashMap<String,Integer> {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public KeywordTable() { super(); }
public int lookup(String name) {
- Object found = get(name);
- if (found == null)
- return -1;
- else
- return ((Integer)found).intValue();
+ return containsKey(name) ? get(name) : -1;
}
public void append(String name, int t) {
- put(name, new Integer(t));
+ put(name, t);
}
}
diff --git a/src/main/javassist/compiler/Lex.java b/src/main/javassist/compiler/Lex.java
index 5233a25..498b22f 100644
--- a/src/main/javassist/compiler/Lex.java
+++ b/src/main/javassist/compiler/Lex.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -31,6 +32,7 @@ public class Lex implements TokenId {
private Token lookAheadTokens;
private String input;
+ @SuppressWarnings("unused")
private int position, maxlen, lineNumber;
/**
@@ -51,12 +53,10 @@ public class Lex implements TokenId {
public int get() {
if (lookAheadTokens == null)
return get(currentToken);
- else {
- Token t;
- currentToken = t = lookAheadTokens;
- lookAheadTokens = lookAheadTokens.next;
- return t.tokenId;
- }
+ Token t;
+ currentToken = t = lookAheadTokens;
+ lookAheadTokens = lookAheadTokens.next;
+ return t.tokenId;
}
/**
@@ -128,15 +128,12 @@ public class Lex implements TokenId {
tbuf.append('.');
return readDouble(tbuf, c, token);
}
- else{
- ungetc(c);
- return readSeparator('.');
- }
+ ungetc(c);
+ return readSeparator('.');
}
else if (Character.isJavaIdentifierStart((char)c))
return readIdentifier(c, token);
- else
- return readSeparator(c);
+ return readSeparator(c);
}
private int getNextNonWhiteChar() {
@@ -248,19 +245,17 @@ public class Lex implements TokenId {
for (;;) {
c = getc();
if ('0' <= c && c <= '9')
- value = value * 16 + (long)(c - '0');
+ value = value * 16 + (c - '0');
else if ('A' <= c && c <= 'F')
- value = value * 16 + (long)(c - 'A' + 10);
+ value = value * 16 + (c - 'A' + 10);
else if ('a' <= c && c <= 'f')
- value = value * 16 + (long)(c - 'a' + 10);
+ value = value * 16 + (c - 'a' + 10);
else {
token.longValue = value;
if (c == 'L' || c == 'l')
return LongConstant;
- else {
- ungetc(c);
- return IntConstant;
- }
+ ungetc(c);
+ return IntConstant;
}
}
else if ('0' <= c2 && c2 <= '7') {
@@ -268,15 +263,13 @@ public class Lex implements TokenId {
for (;;) {
c = getc();
if ('0' <= c && c <= '7')
- value = value * 8 + (long)(c - '0');
+ value = value * 8 + (c - '0');
else {
token.longValue = value;
if (c == 'L' || c == 'l')
return LongConstant;
- else {
- ungetc(c);
- return IntConstant;
- }
+ ungetc(c);
+ return IntConstant;
}
}
}
@@ -289,7 +282,7 @@ public class Lex implements TokenId {
token.longValue = value;
if (c2 == 'F' || c2 == 'f') {
- token.doubleValue = (double)value;
+ token.doubleValue = value;
return FloatConstant;
}
else if (c2 == 'E' || c2 == 'e'
@@ -342,12 +335,10 @@ public class Lex implements TokenId {
if (c == 'F' || c == 'f')
return FloatConstant;
- else {
- if (c != 'D' && c != 'd')
- ungetc(c);
+ if (c != 'D' && c != 'd')
+ ungetc(c);
- return DoubleConstant;
- }
+ return DoubleConstant;
}
// !"#$%&'( )*+,-./0 12345678 9:;<=>?
@@ -361,51 +352,45 @@ public class Lex implements TokenId {
int c2, c3;
if ('!' <= c && c <= '?') {
int t = equalOps[c - '!'];
- if (t == 0)
+ if (t == 0)
return c;
- else {
- c2 = getc();
- if (c == c2)
- switch (c) {
- case '=' :
- return EQ;
- case '+' :
- return PLUSPLUS;
- case '-' :
- return MINUSMINUS;
- case '&' :
- return ANDAND;
- case '<' :
- c3 = getc();
- if (c3 == '=')
- return LSHIFT_E;
- else {
- ungetc(c3);
- return LSHIFT;
- }
- case '>' :
+ c2 = getc();
+ if (c == c2)
+ switch (c) {
+ case '=' :
+ return EQ;
+ case '+' :
+ return PLUSPLUS;
+ case '-' :
+ return MINUSMINUS;
+ case '&' :
+ return ANDAND;
+ case '<' :
+ c3 = getc();
+ if (c3 == '=')
+ return LSHIFT_E;
+ ungetc(c3);
+ return LSHIFT;
+ case '>' :
+ c3 = getc();
+ if (c3 == '=')
+ return RSHIFT_E;
+ else if (c3 == '>') {
c3 = getc();
if (c3 == '=')
- return RSHIFT_E;
- else if (c3 == '>') {
- c3 = getc();
- if (c3 == '=')
- return ARSHIFT_E;
- else {
- ungetc(c3);
- return ARSHIFT;
- }
- }
- else {
- ungetc(c3);
- return RSHIFT;
- }
- default :
- break;
+ return ARSHIFT_E;
+ ungetc(c3);
+ return ARSHIFT;
}
- else if (c2 == '=')
- return t;
- }
+ else {
+ ungetc(c3);
+ return RSHIFT;
+ }
+ default :
+ break;
+ }
+ else if (c2 == '=')
+ return t;
}
else if (c == '^') {
c2 = getc();
@@ -441,17 +426,15 @@ public class Lex implements TokenId {
int t = ktable.lookup(name);
if (t >= 0)
return t;
- else {
- /* tbuf.toString() is executed quickly since it does not
- * need memory copy. Using a hand-written extensible
- * byte-array class instead of StringBuffer is not a good idea
- * for execution speed. Converting a byte array to a String
- * object is very slow. Using an extensible char array
- * might be OK.
- */
- token.textValue = name;
- return Identifier;
- }
+ /* tbuf.toString() is executed quickly since it does not
+ * need memory copy. Using a hand-written extensible
+ * byte-array class instead of StringBuffer is not a good idea
+ * for execution speed. Converting a byte array to a String
+ * object is very slow. Using an extensible char array
+ * might be OK.
+ */
+ token.textValue = name;
+ return Identifier;
}
private static final KeywordTable ktable = new KeywordTable();
@@ -515,6 +498,7 @@ public class Lex implements TokenId {
|| c == '\n';
}
+ @SuppressWarnings("unused")
private static boolean isDigit(int c) {
return '0' <= c && c <= '9';
}
@@ -541,10 +525,8 @@ public class Lex implements TokenId {
return input.charAt(position++);
else
return -1;
- else {
- int c = lastChar;
- lastChar = -1;
- return c;
- }
+ int c = lastChar;
+ lastChar = -1;
+ return c;
}
}
diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java
index cbff0a4..ad25481 100644
--- a/src/main/javassist/compiler/MemberCodeGen.java
+++ b/src/main/javassist/compiler/MemberCodeGen.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,36 @@
package javassist.compiler;
-import javassist.*;
-import javassist.bytecode.*;
-import javassist.compiler.ast.*;
-
import java.util.ArrayList;
+import java.util.List;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.Modifier;
+import javassist.NotFoundException;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.FieldInfo;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+import javassist.compiler.ast.ASTList;
+import javassist.compiler.ast.ASTree;
+import javassist.compiler.ast.ArrayInit;
+import javassist.compiler.ast.CallExpr;
+import javassist.compiler.ast.Declarator;
+import javassist.compiler.ast.Expr;
+import javassist.compiler.ast.Keyword;
+import javassist.compiler.ast.Member;
+import javassist.compiler.ast.MethodDecl;
+import javassist.compiler.ast.NewExpr;
+import javassist.compiler.ast.Pair;
+import javassist.compiler.ast.Stmnt;
+import javassist.compiler.ast.Symbol;
/* Code generator methods depending on javassist.* classes.
*/
@@ -45,8 +71,7 @@ public class MemberCodeGen extends CodeGen {
ClassFile cf = thisClass.getClassFile2();
if (cf == null)
return ClassFile.MAJOR_VERSION; // JDK 1.3
- else
- return cf.getMajorVersion();
+ return cf.getMajorVersion();
}
/**
@@ -63,6 +88,7 @@ public class MemberCodeGen extends CodeGen {
/**
* Returns the JVM-internal representation of this class name.
*/
+ @Override
protected String getThisName() {
return MemberResolver.javaToJvmName(thisClass.getName());
}
@@ -70,11 +96,13 @@ public class MemberCodeGen extends CodeGen {
/**
* Returns the JVM-internal representation of this super class name.
*/
+ @Override
protected String getSuperName() throws CompileError {
return MemberResolver.javaToJvmName(
MemberResolver.getSuperclass(thisClass).getName());
}
+ @Override
protected void insertDefaultSuperCall() throws CompileError {
bytecode.addAload(0);
bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass),
@@ -82,13 +110,13 @@ public class MemberCodeGen extends CodeGen {
}
static class JsrHook extends ReturnHook {
- ArrayList jsrList;
+ List<int[]> jsrList;
CodeGen cgen;
int var;
JsrHook(CodeGen gen) {
super(gen);
- jsrList = new ArrayList();
+ jsrList = new ArrayList<int[]>();
cgen = gen;
var = -1;
}
@@ -108,6 +136,7 @@ public class MemberCodeGen extends CodeGen {
b.addIndex(0);
}
+ @Override
protected boolean doit(Bytecode b, int opcode) {
switch (opcode) {
case Opcode.RETURN :
@@ -156,6 +185,7 @@ public class MemberCodeGen extends CodeGen {
var = retTarget[1];
}
+ @Override
protected boolean doit(Bytecode b, int opcode) {
switch (opcode) {
case Opcode.RETURN :
@@ -185,6 +215,7 @@ public class MemberCodeGen extends CodeGen {
}
}
+ @Override
protected void atTryStmnt(Stmnt st) throws CompileError {
Bytecode bc = bytecode;
Stmnt body = (Stmnt)st.getLeft();
@@ -193,7 +224,7 @@ public class MemberCodeGen extends CodeGen {
ASTList catchList = (ASTList)st.getRight().getLeft();
Stmnt finallyBlock = (Stmnt)st.getRight().getRight().getLeft();
- ArrayList gotoList = new ArrayList();
+ List<Integer> gotoList = new ArrayList<Integer>();
JsrHook jsrHook = null;
if (finallyBlock != null)
@@ -208,7 +239,7 @@ public class MemberCodeGen extends CodeGen {
boolean tryNotReturn = !hasReturned;
if (tryNotReturn) {
bc.addOpcode(Opcode.GOTO);
- gotoList.add(new Integer(bc.currentPc()));
+ gotoList.add(bc.currentPc());
bc.addIndex(0); // correct later
}
@@ -234,7 +265,7 @@ public class MemberCodeGen extends CodeGen {
if (!hasReturned) {
bc.addOpcode(Opcode.GOTO);
- gotoList.add(new Integer(bc.currentPc()));
+ gotoList.add(bc.currentPc());
bc.addIndex(0); // correct later
tryNotReturn = true;
}
@@ -269,13 +300,11 @@ public class MemberCodeGen extends CodeGen {
/**
* Adds a finally clause for earch return statement.
*/
- private void addFinally(ArrayList returnList, Stmnt finallyBlock)
+ private void addFinally(List<int[]> returnList, Stmnt finallyBlock)
throws CompileError
{
Bytecode bc = bytecode;
- int n = returnList.size();
- for (int i = 0; i < n; ++i) {
- final int[] ret = (int[])returnList.get(i);
+ for (final int[] ret:returnList) {
int pc = ret[0];
bc.write16bit(pc, bc.currentPc() - pc + 1);
ReturnHook hook = new JsrHook2(this, ret);
@@ -288,6 +317,7 @@ public class MemberCodeGen extends CodeGen {
}
}
+ @Override
public void atNewExpr(NewExpr expr) throws CompileError {
if (expr.isArray())
atNewArrayExpr(expr);
@@ -407,11 +437,13 @@ public class MemberCodeGen extends CodeGen {
throw new CompileError("bad new expression");
}
+ @Override
protected void atArrayVariableAssign(ArrayInit init, int varType,
int varArray, String varClass) throws CompileError {
atNewArrayExpr2(varType, null, varClass, init);
}
+ @Override
public void atArrayInit(ArrayInit init) throws CompileError {
throw new CompileError("array initializer is not supported");
}
@@ -445,6 +477,7 @@ public class MemberCodeGen extends CodeGen {
bytecode.addMultiNewarray(desc, count);
}
+ @Override
public void atCallExpr(CallExpr expr) throws CompileError {
String mname = null;
CtClass targetClass = null;
@@ -471,8 +504,7 @@ public class MemberCodeGen extends CodeGen {
targetClass = thisClass;
if (inStaticMethod)
throw new CompileError("a constructor cannot be static");
- else
- bytecode.addAload(0); // this
+ bytecode.addAload(0); // this
if (((Keyword)method).get() == SUPER)
targetClass = MemberResolver.getSuperclass(targetClass);
@@ -488,31 +520,44 @@ public class MemberCodeGen extends CodeGen {
}
else if (op == '.') {
ASTree target = e.oprand1();
- if (target instanceof Keyword)
- if (((Keyword)target).get() == SUPER)
- isSpecial = true;
-
- try {
- target.accept(this);
+ String classFollowedByDotSuper = TypeChecker.isDotSuper(target);
+ if (classFollowedByDotSuper != null) {
+ isSpecial = true;
+ targetClass = MemberResolver.getSuperInterface(thisClass,
+ classFollowedByDotSuper);
+ if (inStaticMethod || (cached != null && cached.isStatic()))
+ isStatic = true; // should be static
+ else {
+ aload0pos = bytecode.currentPc();
+ bytecode.addAload(0); // this
+ }
}
- catch (NoFieldException nfe) {
- if (nfe.getExpr() != target)
- throw nfe;
-
- // it should be a static method.
- exprType = CLASS;
- arrayDim = 0;
- className = nfe.getField(); // JVM-internal
- resolver.recordPackage(className);
- isStatic = true;
+ else {
+ if (target instanceof Keyword)
+ if (((Keyword)target).get() == SUPER)
+ isSpecial = true;
+
+ try {
+ target.accept(this);
+ }
+ catch (NoFieldException nfe) {
+ if (nfe.getExpr() != target)
+ throw nfe;
+
+ // it should be a static method.
+ exprType = CLASS;
+ arrayDim = 0;
+ className = nfe.getField(); // JVM-internal
+ isStatic = true;
+ }
+
+ if (arrayDim > 0)
+ targetClass = resolver.lookupClass(javaLangObject, true);
+ else if (exprType == CLASS /* && arrayDim == 0 */)
+ targetClass = resolver.lookupClassByJvmName(className);
+ else
+ badMethod();
}
-
- if (arrayDim > 0)
- targetClass = resolver.lookupClass(javaLangObject, true);
- else if (exprType == CLASS /* && arrayDim == 0 */)
- targetClass = resolver.lookupClassByJvmName(className);
- else
- badMethod();
}
else
badMethod();
@@ -549,14 +594,12 @@ public class MemberCodeGen extends CodeGen {
isStatic = true;
}
+ @SuppressWarnings("unused")
int stack = bytecode.getStackDepth();
// generate code for evaluating arguments.
atMethodArgs(args, types, dims, cnames);
- // used by invokeinterface
- int count = bytecode.getStackDepth() - stack + 1;
-
if (found == null)
found = resolver.lookupMethod(targetClass, thisClass, thisMethod,
mname, types, dims, cnames);
@@ -573,12 +616,12 @@ public class MemberCodeGen extends CodeGen {
}
atMethodCallCore2(targetClass, mname, isStatic, isSpecial,
- aload0pos, count, found);
+ aload0pos, found);
}
private void atMethodCallCore2(CtClass targetClass, String mname,
boolean isStatic, boolean isSpecial,
- int aload0pos, int count,
+ int aload0pos,
MemberResolver.Method found)
throws CompileError
{
@@ -590,7 +633,7 @@ public class MemberCodeGen extends CodeGen {
if (mname.equals(MethodInfo.nameInit)) {
isSpecial = true;
if (declClass != targetClass)
- throw new CompileError("no such constructor");
+ throw new CompileError("no such constructor: " + targetClass.getName());
if (declClass != thisClass && AccessFlag.isPrivate(acc)) {
desc = getAccessibleConstructor(desc, declClass, minfo);
@@ -631,14 +674,16 @@ public class MemberCodeGen extends CodeGen {
bytecode.addInvokestatic(declClass, mname, desc);
}
else if (isSpecial) // if (isSpecial && notStatic(acc))
- bytecode.addInvokespecial(declClass, mname, desc);
+ bytecode.addInvokespecial(targetClass, mname, desc);
else {
if (!Modifier.isPublic(declClass.getModifiers())
|| declClass.isInterface() != targetClass.isInterface())
declClass = targetClass;
- if (declClass.isInterface())
- bytecode.addInvokeinterface(declClass, mname, desc, count);
+ if (declClass.isInterface()) {
+ int nargs = Descriptor.paramSize(desc) + 1;
+ bytecode.addInvokeinterface(declClass, mname, desc, nargs);
+ }
else
if (isStatic)
throw new CompileError(mname + " is not static");
@@ -771,6 +816,7 @@ public class MemberCodeGen extends CodeGen {
}
}
+ @Override
protected void atFieldAssign(Expr expr, int op, ASTree left,
ASTree right, boolean doDup) throws CompileError
{
@@ -829,7 +875,7 @@ public class MemberCodeGen extends CodeGen {
bytecode.add(PUTFIELD);
bytecode.growStack(is2byte ? -3 : -2);
}
-
+
bytecode.addIndex(fi);
}
else {
@@ -845,10 +891,12 @@ public class MemberCodeGen extends CodeGen {
/* overwritten in JvstCodeGen.
*/
+ @Override
public void atMember(Member mem) throws CompileError {
atFieldRead(mem);
}
+ @Override
protected void atFieldRead(ASTree expr) throws CompileError
{
CtField f = fieldAccess(expr, true);
@@ -891,20 +939,18 @@ public class MemberCodeGen extends CodeGen {
minfo.getDescriptor());
return 0;
}
+ int fi = addFieldrefInfo(f, finfo);
+ if (isStatic) {
+ bytecode.add(GETSTATIC);
+ bytecode.growStack(is2byte ? 2 : 1);
+ }
else {
- int fi = addFieldrefInfo(f, finfo);
- if (isStatic) {
- bytecode.add(GETSTATIC);
- bytecode.growStack(is2byte ? 2 : 1);
- }
- else {
- bytecode.add(GETFIELD);
- bytecode.growStack(is2byte ? 1 : 0);
- }
-
- bytecode.addIndex(fi);
- return fi;
+ bytecode.add(GETFIELD);
+ bytecode.growStack(is2byte ? 1 : 0);
}
+
+ bytecode.addIndex(fi);
+ return fi;
}
/**
@@ -916,18 +962,15 @@ public class MemberCodeGen extends CodeGen {
throws CompileError
{
if (AccessFlag.isPrivate(finfo.getAccessFlags())
- && f.getDeclaringClass() != thisClass) {
- CtClass declClass = f.getDeclaringClass();
+ && f.getDeclaringClass() != thisClass) {
+ CtClass declClass = f.getDeclaringClass();
if (isEnclosing(declClass, thisClass)) {
AccessorMaker maker = declClass.getAccessorMaker();
if (maker != null)
return maker;
- else
- throw new CompileError("fatal error. bug?");
}
- else
- throw new CompileError("Field " + f.getName() + " in "
- + declClass.getName() + " is private.");
+ throw new CompileError("Field " + f.getName() + " in "
+ + declClass.getName() + " is private.");
}
return null; // accessible field
@@ -957,7 +1000,7 @@ public class MemberCodeGen extends CodeGen {
else
className = null;
- boolean is2byte = (c == 'J' || c == 'D');
+ boolean is2byte = dim == 0 && (c == 'J' || c == 'D');
return is2byte;
}
@@ -970,6 +1013,7 @@ public class MemberCodeGen extends CodeGen {
return cp.addFieldrefInfo(ci, name, type);
}
+ @Override
protected void atClassObject2(String cname) throws CompileError {
if (getMajorVersion() < ClassFile.JAVA_5)
super.atClassObject2(cname);
@@ -977,6 +1021,7 @@ public class MemberCodeGen extends CodeGen {
bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname));
}
+ @Override
protected void atFieldPlusPlus(int token, boolean isPost,
ASTree oprand, Expr expr, boolean doDup)
throws CompileError
@@ -1077,7 +1122,6 @@ public class MemberCodeGen extends CodeGen {
Symbol fname = (Symbol)e.oprand2();
String cname = nfe.getField();
f = resolver.lookupFieldByJvmName2(cname, fname, expr);
- resolver.recordPackage(cname);
resultStatic = true;
return f;
}
@@ -1118,16 +1162,14 @@ public class MemberCodeGen extends CodeGen {
ASTList list = md.getThrows();
if (list == null)
return null;
- else {
- int i = 0;
- clist = new CtClass[list.length()];
- while (list != null) {
- clist[i++] = resolver.lookupClassByName((ASTList)list.head());
- list = list.tail();
- }
-
- return clist;
+ int i = 0;
+ clist = new CtClass[list.length()];
+ while (list != null) {
+ clist[i++] = resolver.lookupClassByName((ASTList)list.head());
+ list = list.tail();
}
+
+ return clist;
}
/* Converts a class name into a JVM-internal representation.
@@ -1135,6 +1177,7 @@ public class MemberCodeGen extends CodeGen {
* It may also expand a simple class name to java.lang.*.
* For example, this converts Object into java/lang/Object.
*/
+ @Override
protected String resolveClassName(ASTList name) throws CompileError {
return resolver.resolveClassName(name);
}
@@ -1142,6 +1185,7 @@ public class MemberCodeGen extends CodeGen {
/* Expands a simple class name to java.lang.*.
* For example, this converts Object into java/lang/Object.
*/
+ @Override
protected String resolveClassName(String jvmName) throws CompileError {
return resolver.resolveJvmClassName(jvmName);
}
diff --git a/src/main/javassist/compiler/MemberResolver.java b/src/main/javassist/compiler/MemberResolver.java
index fb2aa9b..00dac6c 100644
--- a/src/main/javassist/compiler/MemberResolver.java
+++ b/src/main/javassist/compiler/MemberResolver.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,28 @@
package javassist.compiler;
-import java.util.List;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.Hashtable;
import java.util.Iterator;
-import javassist.*;
-import javassist.bytecode.*;
-import javassist.compiler.ast.*;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.Modifier;
+import javassist.NotFoundException;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.MethodInfo;
+import javassist.compiler.ast.ASTList;
+import javassist.compiler.ast.ASTree;
+import javassist.compiler.ast.Declarator;
+import javassist.compiler.ast.Keyword;
+import javassist.compiler.ast.Symbol;
/* Code generator methods depending on javassist.* classes.
*/
@@ -36,22 +54,6 @@ public class MemberResolver implements TokenId {
throw new CompileError("fatal");
}
- /**
- * @param jvmClassName a class name. Not a package name.
- */
- public void recordPackage(String jvmClassName) {
- String classname = jvmToJavaName(jvmClassName);
- for (;;) {
- int i = classname.lastIndexOf('.');
- if (i > 0) {
- classname = classname.substring(0, i);
- classPool.recordInvalidClassName(classname);
- }
- else
- break;
- }
- }
-
public static class Method {
public CtClass declaring;
public MethodInfo info;
@@ -88,8 +90,7 @@ public class MemberResolver implements TokenId {
Method r = new Method(clazz, current, res);
if (res == YES)
return r;
- else
- maybe = r;
+ maybe = r;
}
}
@@ -97,8 +98,7 @@ public class MemberResolver implements TokenId {
argClassNames, maybe != null);
if (m != null)
return m;
- else
- return maybe;
+ return maybe;
}
private Method lookupMethod(CtClass clazz, String methodName,
@@ -111,11 +111,10 @@ public class MemberResolver implements TokenId {
// If the class is an array type, the class file is null.
// If so, search the super class java.lang.Object for clone() etc.
if (cf != null) {
- List list = cf.getMethods();
- int n = list.size();
- for (int i = 0; i < n; ++i) {
- MethodInfo minfo = (MethodInfo)list.get(i);
- if (minfo.getName().equals(methodName)) {
+ List<MethodInfo> list = cf.getMethods();
+ for (MethodInfo minfo:list) {
+ if (minfo.getName().equals(methodName)
+ && (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0) {
int res = compareSignature(minfo.getDescriptor(),
argTypes, argDims, argClassNames);
if (res != NO) {
@@ -132,7 +131,8 @@ public class MemberResolver implements TokenId {
if (onlyExact)
maybe = null;
else
- onlyExact = maybe != null;
+ if (maybe != null)
+ return maybe;
int mod = clazz.getModifiers();
boolean isIntf = Modifier.isInterface(mod);
@@ -150,30 +150,28 @@ public class MemberResolver implements TokenId {
}
catch (NotFoundException e) {}
- if (isIntf || Modifier.isAbstract(mod))
- try {
- CtClass[] ifs = clazz.getInterfaces();
- int size = ifs.length;
- for (int i = 0; i < size; ++i) {
- Method r = lookupMethod(ifs[i], methodName,
- argTypes, argDims, argClassNames,
- onlyExact);
+ try {
+ CtClass[] ifs = clazz.getInterfaces();
+ for (CtClass intf:ifs) {
+ Method r = lookupMethod(intf, methodName,
+ argTypes, argDims, argClassNames,
+ onlyExact);
+ if (r != null)
+ return r;
+ }
+
+ if (isIntf) {
+ // finally search java.lang.Object.
+ CtClass pclazz = clazz.getSuperclass();
+ if (pclazz != null) {
+ Method r = lookupMethod(pclazz, methodName, argTypes,
+ argDims, argClassNames, onlyExact);
if (r != null)
return r;
}
-
- if (isIntf) {
- // finally search java.lang.Object.
- CtClass pclazz = clazz.getSuperclass();
- if (pclazz != null) {
- Method r = lookupMethod(pclazz, methodName, argTypes,
- argDims, argClassNames, onlyExact);
- if (r != null)
- return r;
- }
- }
}
- catch (NotFoundException e) {}
+ }
+ catch (NotFoundException e) {}
return maybe;
}
@@ -275,6 +273,7 @@ public class MemberResolver implements TokenId {
* Only used by fieldAccess() in MemberCodeGen and TypeChecker.
*
* @param jvmClassName a JVM class name. e.g. java/lang/String
+ * @see #lookupClass(String, boolean)
*/
public CtField lookupFieldByJvmName2(String jvmClassName, Symbol fieldSym,
ASTree expr) throws NoFieldException
@@ -309,7 +308,7 @@ public class MemberResolver implements TokenId {
}
/**
- * @param name a qualified class name. e.g. java.lang.String
+ * @param className a qualified class name. e.g. java.lang.String
*/
public CtField lookupField(String className, Symbol fieldName)
throws CompileError
@@ -336,7 +335,7 @@ public class MemberResolver implements TokenId {
}
/**
- * @parma classname jvm class name.
+ * @param classname jvm class name.
*/
public CtClass lookupClass(int type, int dim, String classname)
throws CompileError
@@ -405,45 +404,78 @@ public class MemberResolver implements TokenId {
public CtClass lookupClass(String name, boolean notCheckInner)
throws CompileError
{
+ Map<String,String> cache = getInvalidNames();
+ String found = cache.get(name);
+ if (found == INVALID)
+ throw new CompileError("no such class: " + name);
+ else if (found != null)
+ try {
+ return classPool.get(found);
+ }
+ catch (NotFoundException e) {}
+
+ CtClass cc = null;
try {
- return lookupClass0(name, notCheckInner);
+ cc = lookupClass0(name, notCheckInner);
}
catch (NotFoundException e) {
- return searchImports(name);
+ cc = searchImports(name);
+ }
+
+ cache.put(name, cc.getName());
+ return cc;
+ }
+
+ private static final String INVALID = "<invalid>";
+ private static Map<ClassPool, Reference<Map<String,String>>> invalidNamesMap =
+ new WeakHashMap<ClassPool, Reference<Map<String,String>>>();
+ private Map<String,String> invalidNames = null;
+
+ // for unit tests
+ public static int getInvalidMapSize() { return invalidNamesMap.size(); }
+
+ private Map<String,String> getInvalidNames() {
+ Map<String,String> ht = invalidNames;
+ if (ht == null) {
+ synchronized (MemberResolver.class) {
+ Reference<Map<String,String>> ref = invalidNamesMap.get(classPool);
+ if (ref != null)
+ ht = ref.get();
+
+ if (ht == null) {
+ ht = new Hashtable<String,String>();
+ invalidNamesMap.put(classPool, new WeakReference<Map<String,String>>(ht));
+ }
+ }
+
+ invalidNames = ht;
}
+
+ return ht;
}
private CtClass searchImports(String orgName)
throws CompileError
{
if (orgName.indexOf('.') < 0) {
- Iterator it = classPool.getImportedPackages();
+ Iterator<String> it = classPool.getImportedPackages();
while (it.hasNext()) {
- String pac = (String)it.next();
- String fqName = pac + '.' + orgName;
+ String pac = it.next();
+ String fqName = pac.replaceAll("\\.$","") + "." + orgName;
try {
- CtClass cc = classPool.get(fqName);
- // if the class is found,
- classPool.recordInvalidClassName(orgName);
- return cc;
+ return classPool.get(fqName);
}
catch (NotFoundException e) {
- classPool.recordInvalidClassName(fqName);
try {
- if (pac.endsWith("." + orgName)) {
- CtClass cc = classPool.get(pac);
- // if the class is found,
- classPool.recordInvalidClassName(orgName);
- return cc;
- }
- }
- catch (NotFoundException e2) {
- classPool.recordInvalidClassName(pac);
+ if (pac.endsWith("." + orgName))
+ return classPool.get(pac);
}
+ catch (NotFoundException e2) {}
}
}
}
+ getInvalidNames().put(orgName, INVALID);
throw new CompileError("no such class: " + orgName);
}
@@ -459,11 +491,9 @@ public class MemberResolver implements TokenId {
int i = classname.lastIndexOf('.');
if (notCheckInner || i < 0)
throw e;
- else {
- StringBuffer sbuf = new StringBuffer(classname);
- sbuf.setCharAt(i, '$');
- classname = sbuf.toString();
- }
+ StringBuffer sbuf = new StringBuffer(classname);
+ sbuf.setCharAt(i, '$');
+ classname = sbuf.toString();
}
} while (cc == null);
return cc;
@@ -477,8 +507,7 @@ public class MemberResolver implements TokenId {
public String resolveClassName(ASTList name) throws CompileError {
if (name == null)
return null;
- else
- return javaToJvmName(lookupClassByName(name).getName());
+ return javaToJvmName(lookupClassByName(name).getName());
}
/* Expands a simple class name to java.lang.*.
@@ -487,8 +516,7 @@ public class MemberResolver implements TokenId {
public String resolveJvmClassName(String jvmName) throws CompileError {
if (jvmName == null)
return null;
- else
- return javaToJvmName(lookupClassByJvmName(jvmName).getName());
+ return javaToJvmName(lookupClassByJvmName(jvmName).getName());
}
public static CtClass getSuperclass(CtClass c) throws CompileError {
@@ -502,6 +530,19 @@ public class MemberResolver implements TokenId {
+ c.getName());
}
+ public static CtClass getSuperInterface(CtClass c, String interfaceName)
+ throws CompileError
+ {
+ try {
+ CtClass[] intfs = c.getInterfaces();
+ for (int i = 0; i < intfs.length; i++)
+ if (intfs[i].getName().equals(interfaceName))
+ return intfs[i];
+ } catch (NotFoundException e) {}
+ throw new CompileError("cannot find the super inetrface " + interfaceName
+ + " of " + c.getName());
+ }
+
public static String javaToJvmName(String classname) {
return classname.replace('.', '/');
}
diff --git a/src/main/javassist/compiler/NoFieldException.java b/src/main/javassist/compiler/NoFieldException.java
index f6e114b..dd87859 100644
--- a/src/main/javassist/compiler/NoFieldException.java
+++ b/src/main/javassist/compiler/NoFieldException.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -18,6 +19,8 @@ package javassist.compiler;
import javassist.compiler.ast.ASTree;
public class NoFieldException extends CompileError {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private String fieldName;
private ASTree expr;
diff --git a/src/main/javassist/compiler/Parser.java b/src/main/javassist/compiler/Parser.java
index d483814..c67d49e 100644
--- a/src/main/javassist/compiler/Parser.java
+++ b/src/main/javassist/compiler/Parser.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,7 +16,29 @@
package javassist.compiler;
-import javassist.compiler.ast.*;
+import javassist.compiler.ast.ASTList;
+import javassist.compiler.ast.ASTree;
+import javassist.compiler.ast.ArrayInit;
+import javassist.compiler.ast.AssignExpr;
+import javassist.compiler.ast.BinExpr;
+import javassist.compiler.ast.CallExpr;
+import javassist.compiler.ast.CastExpr;
+import javassist.compiler.ast.CondExpr;
+import javassist.compiler.ast.Declarator;
+import javassist.compiler.ast.DoubleConst;
+import javassist.compiler.ast.Expr;
+import javassist.compiler.ast.FieldDecl;
+import javassist.compiler.ast.InstanceOfExpr;
+import javassist.compiler.ast.IntConst;
+import javassist.compiler.ast.Keyword;
+import javassist.compiler.ast.Member;
+import javassist.compiler.ast.MethodDecl;
+import javassist.compiler.ast.NewExpr;
+import javassist.compiler.ast.Pair;
+import javassist.compiler.ast.Stmnt;
+import javassist.compiler.ast.StringL;
+import javassist.compiler.ast.Symbol;
+import javassist.compiler.ast.Variable;
public final class Parser implements TokenId {
private Lex lex;
@@ -33,8 +56,7 @@ public final class Parser implements TokenId {
ASTList mem = parseMember1(tbl);
if (mem instanceof MethodDecl)
return parseMethod2(tbl, (MethodDecl)mem);
- else
- return mem;
+ return mem;
}
/* A method body is not parsed.
@@ -62,8 +84,7 @@ public final class Parser implements TokenId {
d.setVariable(new Symbol(name));
if (isConstructor || lex.lookAhead() == '(')
return parseMethod1(tbl, isConstructor, mods, d);
- else
- return parseField(tbl, mods, d);
+ return parseField(tbl, mods, d);
}
/* field.declaration
@@ -187,11 +208,9 @@ public final class Parser implements TokenId {
int dim = parseArrayDimension();
return new Declarator(t, dim);
}
- else {
- ASTList name = parseClassType(tbl);
- int dim = parseArrayDimension();
- return new Declarator(name, dim);
- }
+ ASTList name = parseClassType(tbl);
+ int dim = parseArrayDimension();
+ return new Declarator(name, dim);
}
private static boolean isBuiltinType(int t) {
@@ -293,8 +312,7 @@ public final class Parser implements TokenId {
lex.get(); // '}'
if (body == null)
return new Stmnt(BLOCK); // empty block
- else
- return body;
+ return body;
}
/* if.statement : IF "(" expression ")" statement
@@ -653,8 +671,7 @@ public final class Parser implements TokenId {
private ASTree parseInitializer(SymbolTable tbl) throws CompileError {
if (lex.lookAhead() == '{')
return parseArrayInitializer(tbl);
- else
- return parseExpression(tbl);
+ return parseExpression(tbl);
}
/* array.initializer :
@@ -725,8 +742,7 @@ public final class Parser implements TokenId {
ASTree elseExpr = parseExpression(tbl);
return new CondExpr(cond, thenExpr, elseExpr);
}
- else
- return cond;
+ return cond;
}
/* logical.or.expr 10 (operator precedence)
@@ -777,8 +793,7 @@ public final class Parser implements TokenId {
int p = getOpPrecedence(t);
if (p == 0)
return expr;
- else
- expr = binaryExpr2(tbl, expr, p);
+ expr = binaryExpr2(tbl, expr, p);
}
}
@@ -791,11 +806,9 @@ public final class Parser implements TokenId {
int dim = parseArrayDimension();
return new InstanceOfExpr(t, dim, expr);
}
- else {
- ASTList name = parseClassType(tbl);
- int dim = parseArrayDimension();
- return new InstanceOfExpr(name, dim, expr);
- }
+ ASTList name = parseClassType(tbl);
+ int dim = parseArrayDimension();
+ return new InstanceOfExpr(name, dim, expr);
}
private ASTree binaryExpr2(SymbolTable tbl, ASTree expr, int prec)
@@ -921,6 +934,7 @@ public final class Parser implements TokenId {
}
private boolean nextIsBuiltinCast() {
+ @SuppressWarnings("unused")
int t;
int i = 2;
while ((t = lex.lookAhead(i++)) == '[')
@@ -948,6 +962,7 @@ public final class Parser implements TokenId {
}
private int nextIsClassType(int i) {
+ @SuppressWarnings("unused")
int t;
while (lex.lookAhead(++i) == '.')
if (lex.lookAhead(++i) != Identifier)
@@ -1000,6 +1015,7 @@ public final class Parser implements TokenId {
* | postfix.expr "." Identifier
* | postfix.expr ( "[" "]" )* "." CLASS
* | postfix.expr "#" Identifier
+ * | postfix.expr "." SUPER
*
* "#" is not an operator of regular Java. It separates
* a class name and a member name in an expression for static member
@@ -1057,9 +1073,10 @@ public final class Parser implements TokenId {
case '.' :
lex.get();
t = lex.get();
- if (t == CLASS) {
+ if (t == CLASS)
expr = parseDotClass(expr, 0);
- }
+ else if (t == SUPER)
+ expr = Expr.make('.', new Symbol(toClassName(expr)), new Keyword(t));
else if (t == Identifier) {
str = lex.getString();
expr = Expr.make('.', expr, new Member(str));
@@ -1114,43 +1131,41 @@ public final class Parser implements TokenId {
String cname = CodeGen.toJvmTypeName(builtinType, dim);
return Expr.make('.', new Symbol(cname), new Member("class"));
}
- else {
- String cname;
- switch(builtinType) {
- case BOOLEAN :
- cname = "java.lang.Boolean";
- break;
- case BYTE :
- cname = "java.lang.Byte";
- break;
- case CHAR :
- cname = "java.lang.Character";
- break;
- case SHORT :
- cname = "java.lang.Short";
- break;
- case INT :
- cname = "java.lang.Integer";
- break;
- case LONG :
- cname = "java.lang.Long";
- break;
- case FLOAT :
- cname = "java.lang.Float";
- break;
- case DOUBLE :
- cname = "java.lang.Double";
- break;
- case VOID :
- cname = "java.lang.Void";
- break;
- default :
- throw new CompileError("invalid builtin type: "
- + builtinType);
- }
-
- return Expr.make(MEMBER, new Symbol(cname), new Member("TYPE"));
+ String cname;
+ switch(builtinType) {
+ case BOOLEAN :
+ cname = "java.lang.Boolean";
+ break;
+ case BYTE :
+ cname = "java.lang.Byte";
+ break;
+ case CHAR :
+ cname = "java.lang.Character";
+ break;
+ case SHORT :
+ cname = "java.lang.Short";
+ break;
+ case INT :
+ cname = "java.lang.Integer";
+ break;
+ case LONG :
+ cname = "java.lang.Long";
+ break;
+ case FLOAT :
+ cname = "java.lang.Float";
+ break;
+ case DOUBLE :
+ cname = "java.lang.Double";
+ break;
+ case VOID :
+ cname = "java.lang.Void";
+ break;
+ default :
+ throw new CompileError("invalid builtin type: "
+ + builtinType);
}
+
+ return Expr.make(MEMBER, new Symbol(cname), new Member("TYPE"));
}
/* method.call : method.expr "(" argument.list ")"
@@ -1233,8 +1248,7 @@ public final class Parser implements TokenId {
decl = tbl.lookup(name);
if (decl == null)
return new Member(name); // this or static member
- else
- return new Variable(name, decl); // local variable
+ return new Variable(name, decl); // local variable
case StringL :
return new StringL(lex.getString());
case NEW :
@@ -1243,8 +1257,7 @@ public final class Parser implements TokenId {
expr = parseExpression(tbl);
if (lex.get() == ')')
return expr;
- else
- throw new CompileError(") is missing", lex);
+ throw new CompileError(") is missing", lex);
default :
if (isBuiltinType(t) || t == VOID) {
int dim = parseArrayDimension();
@@ -1308,13 +1321,11 @@ public final class Parser implements TokenId {
lex.get();
return null;
}
- else {
- ASTree index = parseExpression(tbl);
- if (lex.get() != ']')
- throw new CompileError("] is missing", lex);
+ ASTree index = parseExpression(tbl);
+ if (lex.get() != ']')
+ throw new CompileError("] is missing", lex);
- return index;
- }
+ return index;
}
/* argument.list : "(" [ expression [ "," expression ]* ] ")"
diff --git a/src/main/javassist/compiler/ProceedHandler.java b/src/main/javassist/compiler/ProceedHandler.java
index fd77f0e..1c3cd47 100644
--- a/src/main/javassist/compiler/ProceedHandler.java
+++ b/src/main/javassist/compiler/ProceedHandler.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -22,7 +23,6 @@ import javassist.compiler.ast.ASTList;
* An interface to an object for implementing $proceed().
*
* @see javassist.compiler.JvstCodeGen#setProceedHandler(ProceedHandler, String)
- * @see javassist.compiler.JvstCodeGen#atMethodCall(Expr)
*/
public interface ProceedHandler {
void doit(JvstCodeGen gen, Bytecode b, ASTList args) throws CompileError;
diff --git a/src/main/javassist/compiler/SymbolTable.java b/src/main/javassist/compiler/SymbolTable.java
index 8f35f36..e23f8ac 100644
--- a/src/main/javassist/compiler/SymbolTable.java
+++ b/src/main/javassist/compiler/SymbolTable.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,9 +17,12 @@
package javassist.compiler;
import java.util.HashMap;
+
import javassist.compiler.ast.Declarator;
-public final class SymbolTable extends HashMap {
+public final class SymbolTable extends HashMap<String,Declarator> {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private SymbolTable parent;
public SymbolTable() { this(null); }
@@ -31,11 +35,10 @@ public final class SymbolTable extends HashMap {
public SymbolTable getParent() { return parent; }
public Declarator lookup(String name) {
- Declarator found = (Declarator)get(name);
+ Declarator found = get(name);
if (found == null && parent != null)
return parent.lookup(name);
- else
- return found;
+ return found;
}
public void append(String name, Declarator value) {
diff --git a/src/main/javassist/compiler/SyntaxError.java b/src/main/javassist/compiler/SyntaxError.java
index dcbb937..ed759bb 100644
--- a/src/main/javassist/compiler/SyntaxError.java
+++ b/src/main/javassist/compiler/SyntaxError.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,6 +17,9 @@
package javassist.compiler;
public class SyntaxError extends CompileError {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public SyntaxError(Lex lexer) {
super("syntax error near \"" + lexer.getTextAround() + "\"", lexer);
}
diff --git a/src/main/javassist/compiler/TokenId.java b/src/main/javassist/compiler/TokenId.java
index 6a94768..be8e95d 100644
--- a/src/main/javassist/compiler/TokenId.java
+++ b/src/main/javassist/compiler/TokenId.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/compiler/TypeChecker.java b/src/main/javassist/compiler/TypeChecker.java
index d2cb861..9e01d0a 100644
--- a/src/main/javassist/compiler/TypeChecker.java
+++ b/src/main/javassist/compiler/TypeChecker.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,13 +16,34 @@
package javassist.compiler;
+import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
-import javassist.ClassPool;
import javassist.Modifier;
import javassist.NotFoundException;
-import javassist.compiler.ast.*;
-import javassist.bytecode.*;
+import javassist.bytecode.FieldInfo;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+import javassist.compiler.ast.ASTList;
+import javassist.compiler.ast.ASTree;
+import javassist.compiler.ast.ArrayInit;
+import javassist.compiler.ast.AssignExpr;
+import javassist.compiler.ast.BinExpr;
+import javassist.compiler.ast.CallExpr;
+import javassist.compiler.ast.CastExpr;
+import javassist.compiler.ast.CondExpr;
+import javassist.compiler.ast.Declarator;
+import javassist.compiler.ast.DoubleConst;
+import javassist.compiler.ast.Expr;
+import javassist.compiler.ast.InstanceOfExpr;
+import javassist.compiler.ast.IntConst;
+import javassist.compiler.ast.Keyword;
+import javassist.compiler.ast.Member;
+import javassist.compiler.ast.NewExpr;
+import javassist.compiler.ast.StringL;
+import javassist.compiler.ast.Symbol;
+import javassist.compiler.ast.Variable;
+import javassist.compiler.ast.Visitor;
public class TypeChecker extends Visitor implements Opcode, TokenId {
static final String javaLangObject = "java.lang.Object";
@@ -138,6 +160,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
return resolver.resolveJvmClassName(jvmName);
}
+ @Override
public void atNewExpr(NewExpr expr) throws CompileError {
if (expr.isArray())
atNewArrayExpr(expr);
@@ -176,6 +199,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
}
}
+ @Override
public void atArrayInit(ArrayInit init) throws CompileError {
ASTList list = init;
while (list != null) {
@@ -189,6 +213,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
protected void atMultiNewArray(int type, ASTList classname, ASTList size)
throws CompileError
{
+ @SuppressWarnings("unused")
int count, dim;
dim = size.length();
for (count = 0; size != null; size = size.tail()) {
@@ -208,6 +233,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
className = null;
}
+ @Override
public void atAssignExpr(AssignExpr expr) throws CompileError {
// =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>=
int op = expr.getOperator();
@@ -278,11 +304,13 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
className = cname;
}
+ @Override
public void atCondExpr(CondExpr expr) throws CompileError {
booleanExpr(expr.condExpr());
expr.thenExpr().accept(this);
int type1 = exprType;
int dim1 = arrayDim;
+ @SuppressWarnings("unused")
String cname1 = className;
expr.elseExpr().accept(this);
@@ -301,6 +329,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
* (if the original is not '+') and sets the new expression to the
* left-hand-side expression and null to the right-hand-side expression.
*/
+ @Override
public void atBinExpr(BinExpr expr) throws CompileError {
int token = expr.getOperator();
int k = CodeGen.lookupBinOp(token);
@@ -383,10 +412,8 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
className = "java/lang/StringBuffer";
return makeAppendCall(makeAppendCall(e, left), right);
}
- else {
- computeBinExprType(expr, '+', type1);
- return null;
- }
+ computeBinExprType(expr, '+', type1);
+ return null;
}
private boolean isConstant(BinExpr expr, int op, ASTree left,
@@ -405,13 +432,11 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
if (newExpr == null)
return false; // not a constant expression
- else {
- expr.setOperator('+');
- expr.setOprand1(newExpr);
- expr.setOprand2(null);
- newExpr.accept(this); // for setting exprType, arrayDim, ...
- return true;
- }
+ expr.setOperator('+');
+ expr.setOprand1(newExpr);
+ expr.setOprand2(null);
+ newExpr.accept(this); // for setting exprType, arrayDim, ...
+ return true;
}
/* CodeGen.atSwitchStmnt() also calls stripPlusExpr().
@@ -501,7 +526,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
else
insertCast(expr, type1, type2);
- if (CodeGen.isP_INT(exprType))
+ if (CodeGen.isP_INT(exprType) && exprType != BOOLEAN)
exprType = INT; // type1 may be BYTE, ...
}
@@ -541,6 +566,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
exprType = type1;
}
+ @Override
public void atCastExpr(CastExpr expr) throws CompileError {
String cname = resolveClassName(expr.getClassName());
expr.getOprand().accept(this);
@@ -549,12 +575,14 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
className = cname;
}
+ @Override
public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError {
expr.getOprand().accept(this);
exprType = BOOLEAN;
arrayDim = 0;
}
+ @Override
public void atExpr(Expr expr) throws CompileError {
// array access, member access,
// (unary) +, (unary) -, ++, --, !, ~
@@ -564,15 +592,21 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
if (token == '.') {
String member = ((Symbol)expr.oprand2()).get();
if (member.equals("length"))
- atArrayLength(expr);
- else if (member.equals("class"))
+ try {
+ atArrayLength(expr);
+ }
+ catch (NoFieldException nfe) {
+ // length might be a class or package name.
+ atFieldRead(expr);
+ }
+ else if (member.equals("class"))
atClassObject(expr); // .class
else
atFieldRead(expr);
}
else if (token == MEMBER) { // field read
String member = ((Symbol)expr.oprand2()).get();
- if (member.equals("class"))
+ if (member.equals("class"))
atClassObject(expr); // .class
else
atFieldRead(expr);
@@ -622,6 +656,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
return true;
}
+ @Override
public void atCallExpr(CallExpr expr) throws CompileError {
String mname = null;
CtClass targetClass = null;
@@ -649,28 +684,34 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
false);
else if (op == '.') {
ASTree target = e.oprand1();
- try {
- target.accept(this);
+ String classFollowedByDotSuper = isDotSuper(target);
+ if (classFollowedByDotSuper != null)
+ targetClass = MemberResolver.getSuperInterface(thisClass,
+ classFollowedByDotSuper);
+ else {
+ try {
+ target.accept(this);
+ }
+ catch (NoFieldException nfe) {
+ if (nfe.getExpr() != target)
+ throw nfe;
+
+ // it should be a static method.
+ exprType = CLASS;
+ arrayDim = 0;
+ className = nfe.getField(); // JVM-internal
+ e.setOperator(MEMBER);
+ e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(
+ className)));
+ }
+
+ if (arrayDim > 0)
+ targetClass = resolver.lookupClass(javaLangObject, true);
+ else if (exprType == CLASS /* && arrayDim == 0 */)
+ targetClass = resolver.lookupClassByJvmName(className);
+ else
+ badMethod();
}
- catch (NoFieldException nfe) {
- if (nfe.getExpr() != target)
- throw nfe;
-
- // it should be a static method.
- exprType = CLASS;
- arrayDim = 0;
- className = nfe.getField(); // JVM-internal
- e.setOperator(MEMBER);
- e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(
- className)));
- }
-
- if (arrayDim > 0)
- targetClass = resolver.lookupClass(javaLangObject, true);
- else if (exprType == CLASS /* && arrayDim == 0 */)
- targetClass = resolver.lookupClassByJvmName(className);
- else
- badMethod();
}
else
badMethod();
@@ -688,6 +729,26 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
}
/**
+ * Returns non-null if target is something like Foo.super
+ * for accessing the default method in an interface.
+ * Otherwise, null.
+ *
+ * @return the class name followed by {@code .super} or null.
+ */
+ static String isDotSuper(ASTree target) {
+ if (target instanceof Expr) {
+ Expr e = (Expr)target;
+ if (e.getOperator() == '.') {
+ ASTree right = e.oprand2();
+ if (right instanceof Keyword && ((Keyword)right).get() == SUPER)
+ return ((Symbol)e.oprand1()).get();
+ }
+ }
+
+ return null;
+ }
+
+ /**
* @return a pair of the class declaring the invoked method
* and the MethodInfo of that method. Never null.
*/
@@ -706,7 +767,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
mname, types, dims, cnames);
if (found == null) {
String clazz = targetClass.getName();
- String signature = argTypesToString(types, dims, cnames);
+ String signature = argTypesToString(types, dims, cnames);
String msg;
if (mname.equals(MethodInfo.nameInit))
msg = "cannot find constructor " + clazz + signature;
@@ -863,7 +924,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
* often inconvenient, this compiler allows it. The following
* code is for that.
*/
- ASTree oprnd1 = e.oprand1();
+ ASTree oprnd1 = e.oprand1();
if (oprnd1 instanceof Symbol)
return fieldAccess2(e, ((Symbol)oprnd1).get());
@@ -892,6 +953,9 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
public void atArrayLength(Expr expr) throws CompileError {
expr.oprand1().accept(this);
+ if (arrayDim == 0)
+ throw new NoFieldException("length", expr);
+
exprType = INT;
arrayDim = 0;
}
@@ -948,10 +1012,12 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
exprType = INT;
}
+ @Override
public void atMember(Member mem) throws CompileError {
atFieldRead(mem);
}
+ @Override
public void atVariable(Variable v) throws CompileError {
Declarator d = v.getDeclarator();
exprType = d.getType();
@@ -959,6 +1025,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
className = d.getClassName();
}
+ @Override
public void atKeyword(Keyword k) throws CompileError {
arrayDim = 0;
int token = k.get();
@@ -976,19 +1043,21 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
if (token == THIS)
className = getThisName();
else
- className = getSuperName();
+ className = getSuperName();
break;
default :
fatal();
}
}
+ @Override
public void atStringL(StringL s) throws CompileError {
exprType = CLASS;
arrayDim = 0;
className = jvmJavaLangString;
}
+ @Override
public void atIntConst(IntConst i) throws CompileError {
arrayDim = 0;
int type = i.getType();
@@ -998,6 +1067,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
exprType = LONG;
}
+ @Override
public void atDoubleConst(DoubleConst d) throws CompileError {
arrayDim = 0;
if (d.getType() == DoubleConstant)
diff --git a/src/main/javassist/compiler/ast/ASTList.java b/src/main/javassist/compiler/ast/ASTList.java
index b486668..67fbd97 100644
--- a/src/main/javassist/compiler/ast/ASTList.java
+++ b/src/main/javassist/compiler/ast/ASTList.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -22,6 +23,8 @@ import javassist.compiler.CompileError;
* The right subtree must be an ASTList object or null.
*/
public class ASTList extends ASTree {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private ASTree left;
private ASTList right;
@@ -39,12 +42,16 @@ public class ASTList extends ASTree {
return new ASTList(e1, new ASTList(e2, new ASTList(e3)));
}
+ @Override
public ASTree getLeft() { return left; }
+ @Override
public ASTree getRight() { return right; }
+ @Override
public void setLeft(ASTree _left) { left = _left; }
+ @Override
public void setRight(ASTree _right) {
right = (ASTList)_right;
}
@@ -67,8 +74,10 @@ public class ASTList extends ASTree {
right = _tail;
}
+ @Override
public void accept(Visitor v) throws CompileError { v.atASTList(this); }
+ @Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("(<");
@@ -147,13 +156,11 @@ public class ASTList extends ASTree {
public static ASTList concat(ASTList a, ASTList b) {
if (a == null)
return b;
- else {
- ASTList list = a;
- while (list.right != null)
- list = list.right;
+ ASTList list = a;
+ while (list.right != null)
+ list = list.right;
- list.right = b;
- return a;
- }
+ list.right = b;
+ return a;
}
}
diff --git a/src/main/javassist/compiler/ast/ASTree.java b/src/main/javassist/compiler/ast/ASTree.java
index a0c4330..7fd05fd 100644
--- a/src/main/javassist/compiler/ast/ASTree.java
+++ b/src/main/javassist/compiler/ast/ASTree.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,6 +17,7 @@
package javassist.compiler.ast;
import java.io.Serializable;
+
import javassist.compiler.CompileError;
/**
@@ -24,6 +26,9 @@ import javassist.compiler.CompileError;
* and <code>getRight()</code> returns null.
*/
public abstract class ASTree implements Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public ASTree getLeft() { return null; }
public ASTree getRight() { return null; }
@@ -39,6 +44,7 @@ public abstract class ASTree implements Serializable {
*/
public abstract void accept(Visitor v) throws CompileError;
+ @Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append('<');
diff --git a/src/main/javassist/compiler/ast/ArrayInit.java b/src/main/javassist/compiler/ast/ArrayInit.java
index a9c6c2f..f992f42 100644
--- a/src/main/javassist/compiler/ast/ArrayInit.java
+++ b/src/main/javassist/compiler/ast/ArrayInit.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -21,11 +22,16 @@ import javassist.compiler.CompileError;
* Array initializer such as <code>{ 1, 2, 3 }</code>.
*/
public class ArrayInit extends ASTList {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public ArrayInit(ASTree firstElement) {
super(firstElement);
}
+ @Override
public void accept(Visitor v) throws CompileError { v.atArrayInit(this); }
+ @Override
public String getTag() { return "array"; }
}
diff --git a/src/main/javassist/compiler/ast/AssignExpr.java b/src/main/javassist/compiler/ast/AssignExpr.java
index 9e61447..a5e1857 100644
--- a/src/main/javassist/compiler/ast/AssignExpr.java
+++ b/src/main/javassist/compiler/ast/AssignExpr.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -25,6 +26,9 @@ public class AssignExpr extends Expr {
* =, %=, &=, *=, +=, -=, /=, ^=, |=, <<=, >>=, >>>=
*/
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
private AssignExpr(int op, ASTree _head, ASTList _tail) {
super(op, _head, _tail);
}
@@ -34,6 +38,7 @@ public class AssignExpr extends Expr {
return new AssignExpr(op, oprand1, new ASTList(oprand2));
}
+ @Override
public void accept(Visitor v) throws CompileError {
v.atAssignExpr(this);
}
diff --git a/src/main/javassist/compiler/ast/BinExpr.java b/src/main/javassist/compiler/ast/BinExpr.java
index 1fb5a6a..9630ada 100644
--- a/src/main/javassist/compiler/ast/BinExpr.java
+++ b/src/main/javassist/compiler/ast/BinExpr.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -29,6 +30,9 @@ public class BinExpr extends Expr {
* <<, >>, >>>, +, -, *, /, %
*/
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
private BinExpr(int op, ASTree _head, ASTList _tail) {
super(op, _head, _tail);
}
@@ -37,5 +41,6 @@ public class BinExpr extends Expr {
return new BinExpr(op, oprand1, new ASTList(oprand2));
}
+ @Override
public void accept(Visitor v) throws CompileError { v.atBinExpr(this); }
}
diff --git a/src/main/javassist/compiler/ast/CallExpr.java b/src/main/javassist/compiler/ast/CallExpr.java
index 544ce24..395915e 100644
--- a/src/main/javassist/compiler/ast/CallExpr.java
+++ b/src/main/javassist/compiler/ast/CallExpr.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,13 +17,15 @@
package javassist.compiler.ast;
import javassist.compiler.CompileError;
-import javassist.compiler.TokenId;
import javassist.compiler.MemberResolver;
+import javassist.compiler.TokenId;
/**
* Method call expression.
*/
public class CallExpr extends Expr {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private MemberResolver.Method method; // cached result of lookupMethod()
private CallExpr(ASTree _head, ASTList _tail) {
@@ -42,5 +45,6 @@ public class CallExpr extends Expr {
return new CallExpr(target, new ASTList(args));
}
+ @Override
public void accept(Visitor v) throws CompileError { v.atCallExpr(this); }
}
diff --git a/src/main/javassist/compiler/ast/CastExpr.java b/src/main/javassist/compiler/ast/CastExpr.java
index 3fb5640..903e5bb 100644
--- a/src/main/javassist/compiler/ast/CastExpr.java
+++ b/src/main/javassist/compiler/ast/CastExpr.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,13 +16,15 @@
package javassist.compiler.ast;
-import javassist.compiler.TokenId;
import javassist.compiler.CompileError;
+import javassist.compiler.TokenId;
/**
* Cast expression.
*/
public class CastExpr extends ASTList implements TokenId {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected int castType;
protected int arrayDim;
@@ -49,7 +52,9 @@ public class CastExpr extends ASTList implements TokenId {
public void setOprand(ASTree t) { getRight().setLeft(t); }
+ @Override
public String getTag() { return "cast:" + castType + ":" + arrayDim; }
+ @Override
public void accept(Visitor v) throws CompileError { v.atCastExpr(this); }
}
diff --git a/src/main/javassist/compiler/ast/CondExpr.java b/src/main/javassist/compiler/ast/CondExpr.java
index 2fb9603..4643501 100644
--- a/src/main/javassist/compiler/ast/CondExpr.java
+++ b/src/main/javassist/compiler/ast/CondExpr.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -21,6 +22,9 @@ import javassist.compiler.CompileError;
* Conditional expression.
*/
public class CondExpr extends ASTList {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public CondExpr(ASTree cond, ASTree thenp, ASTree elsep) {
super(cond, new ASTList(thenp, new ASTList(elsep)));
}
@@ -37,7 +41,9 @@ public class CondExpr extends ASTList {
public void setElse(ASTree t) { tail().tail().setHead(t); }
+ @Override
public String getTag() { return "?:"; }
+ @Override
public void accept(Visitor v) throws CompileError { v.atCondExpr(this); }
}
diff --git a/src/main/javassist/compiler/ast/Declarator.java b/src/main/javassist/compiler/ast/Declarator.java
index d3a43f0..24170db 100644
--- a/src/main/javassist/compiler/ast/Declarator.java
+++ b/src/main/javassist/compiler/ast/Declarator.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,13 +16,15 @@
package javassist.compiler.ast;
-import javassist.compiler.TokenId;
import javassist.compiler.CompileError;
+import javassist.compiler.TokenId;
/**
* Variable declarator.
*/
public class Declarator extends ASTList implements TokenId {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected int varType;
protected int arrayDim;
protected int localVar;
@@ -85,16 +88,17 @@ public class Declarator extends ASTList implements TokenId {
ASTList t = tail();
if (t != null)
return t.head();
- else
- return null;
+ return null;
}
public void setLocalVar(int n) { localVar = n; }
public int getLocalVar() { return localVar; }
+ @Override
public String getTag() { return "decl"; }
+ @Override
public void accept(Visitor v) throws CompileError {
v.atDeclarator(this);
}
diff --git a/src/main/javassist/compiler/ast/DoubleConst.java b/src/main/javassist/compiler/ast/DoubleConst.java
index 5276c2f..f8d0afd 100644
--- a/src/main/javassist/compiler/ast/DoubleConst.java
+++ b/src/main/javassist/compiler/ast/DoubleConst.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -22,6 +23,8 @@ import javassist.compiler.TokenId;
* Double constant.
*/
public class DoubleConst extends ASTree {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected double value;
protected int type;
@@ -35,8 +38,10 @@ public class DoubleConst extends ASTree {
*/
public int getType() { return type; }
+ @Override
public String toString() { return Double.toString(value); }
+ @Override
public void accept(Visitor v) throws CompileError {
v.atDoubleConst(this);
}
@@ -62,7 +67,7 @@ public class DoubleConst extends ASTree {
}
private DoubleConst compute0(int op, IntConst right) {
- return compute(op, this.value, (double)right.value, this.type);
+ return compute(op, this.value, right.value, this.type);
}
private static DoubleConst compute(int op, double value1, double value2,
diff --git a/src/main/javassist/compiler/ast/Expr.java b/src/main/javassist/compiler/ast/Expr.java
index ec11200..ed5cb60 100644
--- a/src/main/javassist/compiler/ast/Expr.java
+++ b/src/main/javassist/compiler/ast/Expr.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,8 +16,8 @@
package javassist.compiler.ast;
-import javassist.compiler.TokenId;
import javassist.compiler.CompileError;
+import javassist.compiler.TokenId;
/**
* Expression.
@@ -28,6 +29,8 @@ public class Expr extends ASTList implements TokenId {
* Otherwise, the object should be an instance of a subclass.
*/
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected int operatorId;
Expr(int op, ASTree _head, ASTList _tail) {
@@ -64,6 +67,7 @@ public class Expr extends ASTList implements TokenId {
getRight().setLeft(expr);
}
+ @Override
public void accept(Visitor v) throws CompileError { v.atExpr(this); }
public String getName() {
@@ -78,6 +82,7 @@ public class Expr extends ASTList implements TokenId {
return String.valueOf(id);
}
+ @Override
protected String getTag() {
return "op:" + getName();
}
diff --git a/src/main/javassist/compiler/ast/FieldDecl.java b/src/main/javassist/compiler/ast/FieldDecl.java
index ce32b87..e2a066e 100644
--- a/src/main/javassist/compiler/ast/FieldDecl.java
+++ b/src/main/javassist/compiler/ast/FieldDecl.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -18,6 +19,9 @@ package javassist.compiler.ast;
import javassist.compiler.CompileError;
public class FieldDecl extends ASTList {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public FieldDecl(ASTree _head, ASTList _tail) {
super(_head, _tail);
}
@@ -26,8 +30,9 @@ public class FieldDecl extends ASTList {
public Declarator getDeclarator() { return (Declarator)tail().head(); }
- public ASTree getInit() { return (ASTree)sublist(2).head(); }
+ public ASTree getInit() { return sublist(2).head(); }
+ @Override
public void accept(Visitor v) throws CompileError {
v.atFieldDecl(this);
}
diff --git a/src/main/javassist/compiler/ast/InstanceOfExpr.java b/src/main/javassist/compiler/ast/InstanceOfExpr.java
index 9813ce8..ddf07bd 100644
--- a/src/main/javassist/compiler/ast/InstanceOfExpr.java
+++ b/src/main/javassist/compiler/ast/InstanceOfExpr.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -21,6 +22,9 @@ import javassist.compiler.CompileError;
* Instanceof expression.
*/
public class InstanceOfExpr extends CastExpr {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public InstanceOfExpr(ASTList className, int dim, ASTree expr) {
super(className, dim, expr);
}
@@ -29,10 +33,12 @@ public class InstanceOfExpr extends CastExpr {
super(type, dim, expr);
}
+ @Override
public String getTag() {
return "instanceof:" + castType + ":" + arrayDim;
}
+ @Override
public void accept(Visitor v) throws CompileError {
v.atInstanceOfExpr(this);
}
diff --git a/src/main/javassist/compiler/ast/IntConst.java b/src/main/javassist/compiler/ast/IntConst.java
index 703a0bf..7040b0c 100644
--- a/src/main/javassist/compiler/ast/IntConst.java
+++ b/src/main/javassist/compiler/ast/IntConst.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -22,6 +23,8 @@ import javassist.compiler.TokenId;
* Integer constant.
*/
public class IntConst extends ASTree {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected long value;
protected int type;
@@ -35,8 +38,10 @@ public class IntConst extends ASTree {
*/
public int getType() { return type; }
+ @Override
public String toString() { return Long.toString(value); }
+ @Override
public void accept(Visitor v) throws CompileError {
v.atIntConst(this);
}
@@ -110,7 +115,7 @@ public class IntConst extends ASTree {
}
private DoubleConst compute0(int op, DoubleConst right) {
- double value1 = (double)this.value;
+ double value1 = this.value;
double value2 = right.value;
double newValue;
switch (op) {
diff --git a/src/main/javassist/compiler/ast/Keyword.java b/src/main/javassist/compiler/ast/Keyword.java
index a1a9ebf..b509375 100644
--- a/src/main/javassist/compiler/ast/Keyword.java
+++ b/src/main/javassist/compiler/ast/Keyword.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -21,6 +22,8 @@ import javassist.compiler.CompileError;
* Keyword.
*/
public class Keyword extends ASTree {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected int tokenId;
public Keyword(int token) {
@@ -29,7 +32,9 @@ public class Keyword extends ASTree {
public int get() { return tokenId; }
+ @Override
public String toString() { return "id:" + tokenId; }
+ @Override
public void accept(Visitor v) throws CompileError { v.atKeyword(this); }
}
diff --git a/src/main/javassist/compiler/ast/Member.java b/src/main/javassist/compiler/ast/Member.java
index 404f2b8..192c9ef 100644
--- a/src/main/javassist/compiler/ast/Member.java
+++ b/src/main/javassist/compiler/ast/Member.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,13 +16,15 @@
package javassist.compiler.ast;
-import javassist.compiler.CompileError;
import javassist.CtField;
+import javassist.compiler.CompileError;
/**
* Member name.
*/
public class Member extends Symbol {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
// cache maintained by fieldAccess() in TypeChecker.
// this is used to obtain the value of a static final field.
private CtField field;
@@ -35,5 +38,6 @@ public class Member extends Symbol {
public CtField getField() { return field; }
+ @Override
public void accept(Visitor v) throws CompileError { v.atMember(this); }
}
diff --git a/src/main/javassist/compiler/ast/MethodDecl.java b/src/main/javassist/compiler/ast/MethodDecl.java
index 8d0661c..d96e3d4 100644
--- a/src/main/javassist/compiler/ast/MethodDecl.java
+++ b/src/main/javassist/compiler/ast/MethodDecl.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -18,6 +19,8 @@ package javassist.compiler.ast;
import javassist.compiler.CompileError;
public class MethodDecl extends ASTList {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
public static final String initName = "<init>";
public MethodDecl(ASTree _head, ASTList _tail) {
@@ -39,6 +42,7 @@ public class MethodDecl extends ASTList {
public Stmnt getBody() { return (Stmnt)sublist(4).head(); }
+ @Override
public void accept(Visitor v) throws CompileError {
v.atMethodDecl(this);
}
diff --git a/src/main/javassist/compiler/ast/NewExpr.java b/src/main/javassist/compiler/ast/NewExpr.java
index db3cb51..44b264c 100644
--- a/src/main/javassist/compiler/ast/NewExpr.java
+++ b/src/main/javassist/compiler/ast/NewExpr.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,13 +16,15 @@
package javassist.compiler.ast;
-import javassist.compiler.TokenId;
import javassist.compiler.CompileError;
+import javassist.compiler.TokenId;
/**
* New Expression.
*/
public class NewExpr extends ASTList implements TokenId {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected boolean newArray;
protected int arrayType;
@@ -65,12 +68,13 @@ public class NewExpr extends ASTList implements TokenId {
ASTree t = getRight().getRight();
if (t == null)
return null;
- else
- return (ArrayInit)t.getLeft();
+ return (ArrayInit)t.getLeft();
}
+ @Override
public void accept(Visitor v) throws CompileError { v.atNewExpr(this); }
+ @Override
protected String getTag() {
return newArray ? "new[]" : "new";
}
diff --git a/src/main/javassist/compiler/ast/Pair.java b/src/main/javassist/compiler/ast/Pair.java
index 1831a35..840c114 100644
--- a/src/main/javassist/compiler/ast/Pair.java
+++ b/src/main/javassist/compiler/ast/Pair.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -22,6 +23,8 @@ import javassist.compiler.CompileError;
* overriding abstract methods in ASTree.
*/
public class Pair extends ASTree {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected ASTree left, right;
public Pair(ASTree _left, ASTree _right) {
@@ -29,8 +32,10 @@ public class Pair extends ASTree {
right = _right;
}
+ @Override
public void accept(Visitor v) throws CompileError { v.atPair(this); }
+ @Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("(<Pair> ");
@@ -41,11 +46,15 @@ public class Pair extends ASTree {
return sbuf.toString();
}
+ @Override
public ASTree getLeft() { return left; }
+ @Override
public ASTree getRight() { return right; }
+ @Override
public void setLeft(ASTree _left) { left = _left; }
+ @Override
public void setRight(ASTree _right) { right = _right; }
}
diff --git a/src/main/javassist/compiler/ast/Stmnt.java b/src/main/javassist/compiler/ast/Stmnt.java
index d5c6d62..c5aa5df 100644
--- a/src/main/javassist/compiler/ast/Stmnt.java
+++ b/src/main/javassist/compiler/ast/Stmnt.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,13 +16,15 @@
package javassist.compiler.ast;
-import javassist.compiler.TokenId;
import javassist.compiler.CompileError;
+import javassist.compiler.TokenId;
/**
* Statement.
*/
public class Stmnt extends ASTList implements TokenId {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected int operatorId;
public Stmnt(int op, ASTree _head, ASTList _tail) {
@@ -46,14 +49,15 @@ public class Stmnt extends ASTList implements TokenId {
return new Stmnt(op, op1, new ASTList(op2, new ASTList(op3)));
}
+ @Override
public void accept(Visitor v) throws CompileError { v.atStmnt(this); }
public int getOperator() { return operatorId; }
+ @Override
protected String getTag() {
if (operatorId < 128)
return "stmnt:" + (char)operatorId;
- else
- return "stmnt:" + operatorId;
+ return "stmnt:" + operatorId;
}
}
diff --git a/src/main/javassist/compiler/ast/StringL.java b/src/main/javassist/compiler/ast/StringL.java
index b9e5fe8..7c7e00c 100644
--- a/src/main/javassist/compiler/ast/StringL.java
+++ b/src/main/javassist/compiler/ast/StringL.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -21,6 +22,8 @@ import javassist.compiler.CompileError;
* String literal.
*/
public class StringL extends ASTree {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected String text;
public StringL(String t) {
@@ -29,7 +32,9 @@ public class StringL extends ASTree {
public String get() { return text; }
+ @Override
public String toString() { return "\"" + text + "\""; }
+ @Override
public void accept(Visitor v) throws CompileError { v.atStringL(this); }
}
diff --git a/src/main/javassist/compiler/ast/Symbol.java b/src/main/javassist/compiler/ast/Symbol.java
index 0b26e75..2b66207 100644
--- a/src/main/javassist/compiler/ast/Symbol.java
+++ b/src/main/javassist/compiler/ast/Symbol.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -21,6 +22,8 @@ import javassist.compiler.CompileError;
* Identifier.
*/
public class Symbol extends ASTree {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected String identifier;
public Symbol(String sym) {
@@ -29,7 +32,9 @@ public class Symbol extends ASTree {
public String get() { return identifier; }
+ @Override
public String toString() { return identifier; }
+ @Override
public void accept(Visitor v) throws CompileError { v.atSymbol(this); }
}
diff --git a/src/main/javassist/compiler/ast/Variable.java b/src/main/javassist/compiler/ast/Variable.java
index 353690e..c9224a7 100644
--- a/src/main/javassist/compiler/ast/Variable.java
+++ b/src/main/javassist/compiler/ast/Variable.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -21,6 +22,8 @@ import javassist.compiler.CompileError;
* Variable.
*/
public class Variable extends Symbol {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected Declarator declarator;
public Variable(String sym, Declarator d) {
@@ -30,9 +33,11 @@ public class Variable extends Symbol {
public Declarator getDeclarator() { return declarator; }
+ @Override
public String toString() {
return identifier + ":" + declarator.getType();
}
+ @Override
public void accept(Visitor v) throws CompileError { v.atVariable(this); }
}
diff --git a/src/main/javassist/compiler/ast/Visitor.java b/src/main/javassist/compiler/ast/Visitor.java
index b4cbf34..b08df1b 100644
--- a/src/main/javassist/compiler/ast/Visitor.java
+++ b/src/main/javassist/compiler/ast/Visitor.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -20,7 +21,7 @@ import javassist.compiler.CompileError;
/**
* The visitor pattern.
*
- * @see ast.ASTree#accept(Visitor)
+ * @see ASTree#accept(Visitor)
*/
public class Visitor {
public void atASTList(ASTList n) throws CompileError {}
diff --git a/src/main/javassist/convert/TransformAccessArrayField.java b/src/main/javassist/convert/TransformAccessArrayField.java
index f70148f..6dab42a 100644
--- a/src/main/javassist/convert/TransformAccessArrayField.java
+++ b/src/main/javassist/convert/TransformAccessArrayField.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,10 +16,9 @@
package javassist.convert;
import javassist.CannotCompileException;
-import javassist.ClassPool;
+import javassist.CodeConverter.ArrayAccessReplacementMethodNames;
import javassist.CtClass;
import javassist.NotFoundException;
-import javassist.CodeConverter.ArrayAccessReplacementMethodNames;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.CodeIterator;
import javassist.bytecode.ConstPool;
@@ -48,6 +48,7 @@ public final class TransformAccessArrayField extends Transformer {
}
+ @Override
public void initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException {
/*
* This transformer must be isolated from other transformers, since some
@@ -84,11 +85,13 @@ public final class TransformAccessArrayField extends Transformer {
}
}
+ @Override
public void clean() {
frames = null;
offset = -1;
}
+ @Override
public int transform(CtClass tclazz, int pos, CodeIterator iterator,
ConstPool cp) throws BadBytecode {
// Do nothing, see above comment
diff --git a/src/main/javassist/convert/TransformAfter.java b/src/main/javassist/convert/TransformAfter.java
index 6015115..3008609 100644
--- a/src/main/javassist/convert/TransformAfter.java
+++ b/src/main/javassist/convert/TransformAfter.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -17,7 +18,8 @@ package javassist.convert;
import javassist.CtMethod;
import javassist.NotFoundException;
-import javassist.bytecode.*;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.CodeIterator;
public class TransformAfter extends TransformBefore {
public TransformAfter(Transformer next,
@@ -27,6 +29,7 @@ public class TransformAfter extends TransformBefore {
super(next, origMethod, afterMethod);
}
+ @Override
protected int match2(int pos, CodeIterator iterator) throws BadBytecode {
iterator.move(pos);
iterator.insert(saveCode);
diff --git a/src/main/javassist/convert/TransformBefore.java b/src/main/javassist/convert/TransformBefore.java
index 2ad585f..6471b20 100644
--- a/src/main/javassist/convert/TransformBefore.java
+++ b/src/main/javassist/convert/TransformBefore.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -18,7 +19,12 @@ package javassist.convert;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
-import javassist.bytecode.*;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
public class TransformBefore extends TransformCall {
protected CtClass[] parameterTypes;
@@ -41,6 +47,7 @@ public class TransformBefore extends TransformCall {
saveCode = loadCode = null;
}
+ @Override
public void initialize(ConstPool cp, CodeAttribute attr) {
super.initialize(cp, attr);
locals = 0;
@@ -48,6 +55,7 @@ public class TransformBefore extends TransformCall {
saveCode = loadCode = null;
}
+ @Override
protected int match(int c, int pos, CodeIterator iterator,
int typedesc, ConstPool cp) throws BadBytecode
{
@@ -77,6 +85,7 @@ public class TransformBefore extends TransformCall {
return iterator.next();
}
+ @Override
public int extraLocals() { return locals; }
protected void makeCode(CtClass[] paramTypes, ConstPool cp) {
diff --git a/src/main/javassist/convert/TransformCall.java b/src/main/javassist/convert/TransformCall.java
index fb8395e..fefbe8a 100644
--- a/src/main/javassist/convert/TransformCall.java
+++ b/src/main/javassist/convert/TransformCall.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,12 +16,15 @@
package javassist.convert;
+import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
-import javassist.ClassPool;
import javassist.Modifier;
import javassist.NotFoundException;
-import javassist.bytecode.*;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
public class TransformCall extends Transformer {
protected String classname, methodname, methodDescriptor;
@@ -50,6 +54,7 @@ public class TransformCall extends Transformer {
newMethodIsPrivate = Modifier.isPrivate(substMethod.getModifiers());
}
+ @Override
public void initialize(ConstPool cp, CodeAttribute attr) {
if (constPool != cp)
newIndex = 0;
@@ -62,6 +67,7 @@ public class TransformCall extends Transformer {
* by <code>classname</code>. This method transforms the instruction
* in that case unless the subclass overrides the target method.
*/
+ @Override
public int transform(CtClass clazz, int pos, CodeIterator iterator,
ConstPool cp) throws BadBytecode
{
diff --git a/src/main/javassist/convert/TransformFieldAccess.java b/src/main/javassist/convert/TransformFieldAccess.java
index d97c95d..681acc5 100644
--- a/src/main/javassist/convert/TransformFieldAccess.java
+++ b/src/main/javassist/convert/TransformFieldAccess.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,10 +16,12 @@
package javassist.convert;
-import javassist.bytecode.*;
import javassist.CtClass;
import javassist.CtField;
import javassist.Modifier;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
final public class TransformFieldAccess extends Transformer {
private String newClassname, newFieldname;
@@ -42,6 +45,7 @@ final public class TransformFieldAccess extends Transformer {
this.constPool = null;
}
+ @Override
public void initialize(ConstPool cp, CodeAttribute attr) {
if (constPool != cp)
newIndex = 0;
@@ -53,6 +57,7 @@ final public class TransformFieldAccess extends Transformer {
* in a superclass of the class in which the original field is
* declared.
*/
+ @Override
public int transform(CtClass clazz, int pos,
CodeIterator iterator, ConstPool cp)
{
diff --git a/src/main/javassist/convert/TransformNew.java b/src/main/javassist/convert/TransformNew.java
index f796489..23c9c7a 100644
--- a/src/main/javassist/convert/TransformNew.java
+++ b/src/main/javassist/convert/TransformNew.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,14 @@
package javassist.convert;
-import javassist.bytecode.*;
-import javassist.CtClass;
import javassist.CannotCompileException;
+import javassist.CtClass;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.StackMap;
+import javassist.bytecode.StackMapTable;
final public class TransformNew extends Transformer {
private int nested;
@@ -31,6 +37,7 @@ final public class TransformNew extends Transformer {
this.trapMethod = trapMethod;
}
+ @Override
public void initialize(ConstPool cp, CodeAttribute attr) {
nested = 0;
}
@@ -47,6 +54,7 @@ final public class TransformNew extends Transformer {
* ...
* INVOKESTATIC trapMethod in trapClass
*/
+ @Override
public int transform(CtClass clazz, int pos, CodeIterator iterator,
ConstPool cp) throws CannotCompileException
{
diff --git a/src/main/javassist/convert/TransformNewClass.java b/src/main/javassist/convert/TransformNewClass.java
index f34ef83..83f2a0c 100644
--- a/src/main/javassist/convert/TransformNewClass.java
+++ b/src/main/javassist/convert/TransformNewClass.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,11 @@
package javassist.convert;
-import javassist.bytecode.*;
-import javassist.CtClass;
import javassist.CannotCompileException;
+import javassist.CtClass;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
final public class TransformNewClass extends Transformer {
private int nested;
@@ -31,6 +34,7 @@ final public class TransformNewClass extends Transformer {
this.newClassName = newClassName;
}
+ @Override
public void initialize(ConstPool cp, CodeAttribute attr) {
nested = 0;
newClassIndex = newMethodNTIndex = newMethodIndex = 0;
@@ -43,6 +47,7 @@ final public class TransformNewClass extends Transformer {
* ...
* INVOKESPECIAL classname:method
*/
+ @Override
public int transform(CtClass clazz, int pos, CodeIterator iterator,
ConstPool cp) throws CannotCompileException
{
diff --git a/src/main/javassist/convert/TransformReadField.java b/src/main/javassist/convert/TransformReadField.java
index a9e613c..e5e373c 100644
--- a/src/main/javassist/convert/TransformReadField.java
+++ b/src/main/javassist/convert/TransformReadField.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,12 +16,14 @@
package javassist.convert;
-import javassist.bytecode.*;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
-import javassist.NotFoundException;
import javassist.Modifier;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
public class TransformReadField extends Transformer {
protected String fieldname;
@@ -65,6 +68,7 @@ public class TransformReadField extends Transformer {
return false;
}
+ @Override
public int transform(CtClass tclazz, int pos, CodeIterator iterator,
ConstPool cp) throws BadBytecode
{
diff --git a/src/main/javassist/convert/TransformWriteField.java b/src/main/javassist/convert/TransformWriteField.java
index 2b6f8bb..dd3ddb1 100644
--- a/src/main/javassist/convert/TransformWriteField.java
+++ b/src/main/javassist/convert/TransformWriteField.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -17,7 +18,10 @@ package javassist.convert;
import javassist.CtClass;
import javassist.CtField;
-import javassist.bytecode.*;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
final public class TransformWriteField extends TransformReadField {
public TransformWriteField(Transformer next, CtField field,
@@ -26,6 +30,7 @@ final public class TransformWriteField extends TransformReadField {
super(next, field, methodClassname, methodName);
}
+ @Override
public int transform(CtClass tclazz, int pos, CodeIterator iterator,
ConstPool cp) throws BadBytecode
{
diff --git a/src/main/javassist/convert/Transformer.java b/src/main/javassist/convert/Transformer.java
index 4dd3807..0b03432 100644
--- a/src/main/javassist/convert/Transformer.java
+++ b/src/main/javassist/convert/Transformer.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/expr/Cast.java b/src/main/javassist/expr/Cast.java
index 1ef87be..31522e5 100644
--- a/src/main/javassist/expr/Cast.java
+++ b/src/main/javassist/expr/Cast.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,23 @@
package javassist.expr;
-import javassist.*;
-import javassist.bytecode.*;
-import javassist.compiler.*;
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
+import javassist.compiler.JvstCodeGen;
+import javassist.compiler.JvstTypeChecker;
+import javassist.compiler.ProceedHandler;
import javassist.compiler.ast.ASTList;
/**
@@ -35,6 +50,7 @@ public class Cast extends Expr {
* Returns the method or constructor containing the type cast
* expression represented by this object.
*/
+ @Override
public CtBehavior where() { return super.where(); }
/**
@@ -43,6 +59,7 @@ public class Cast extends Expr {
*
* @return -1 if this information is not available.
*/
+ @Override
public int getLineNumber() {
return super.getLineNumber();
}
@@ -52,6 +69,7 @@ public class Cast extends Expr {
*
* @return null if this information is not available.
*/
+ @Override
public String getFileName() {
return super.getFileName();
}
@@ -74,6 +92,7 @@ public class Cast extends Expr {
* including the expression can catch and the exceptions that
* the throws declaration allows the method to throw.
*/
+ @Override
public CtClass[] mayThrow() {
return super.mayThrow();
}
@@ -86,8 +105,10 @@ public class Cast extends Expr {
*
* @param statement a Java statement except try-catch.
*/
+ @Override
public void replace(String statement) throws CannotCompileException {
thisClass.getClassFile(); // to call checkModify().
+ @SuppressWarnings("unused")
ConstPool constPool = getConstPool();
int pos = currentPos;
int index = iterator.u16bitAt(pos + 1);
@@ -141,6 +162,7 @@ public class Cast extends Expr {
retType = t;
}
+ @Override
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
throws CompileError
{
@@ -154,7 +176,8 @@ public class Cast extends Expr {
bytecode.addIndex(index);
gen.setType(retType);
}
-
+
+ @Override
public void setReturnType(JvstTypeChecker c, ASTList args)
throws CompileError
{
diff --git a/src/main/javassist/expr/ConstructorCall.java b/src/main/javassist/expr/ConstructorCall.java
index 3a6a02f..343e2a0 100644
--- a/src/main/javassist/expr/ConstructorCall.java
+++ b/src/main/javassist/expr/ConstructorCall.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -39,6 +40,7 @@ public class ConstructorCall extends MethodCall {
/**
* Returns <code>"super"</code> or "<code>"this"</code>.
*/
+ @Override
public String getMethodName() {
return isSuper() ? "super" : "this";
}
@@ -48,6 +50,7 @@ public class ConstructorCall extends MethodCall {
*
* @see #getConstructor()
*/
+ @Override
public CtMethod getMethod() throws NotFoundException {
throw new NotFoundException("this is a constructor call. Call getConstructor().");
}
@@ -63,6 +66,7 @@ public class ConstructorCall extends MethodCall {
* Returns true if the called constructor is not <code>this()</code>
* but <code>super()</code> (a constructor declared in the super class).
*/
+ @Override
public boolean isSuper() {
return super.isSuper();
}
diff --git a/src/main/javassist/expr/Expr.java b/src/main/javassist/expr/Expr.java
index 75de54a..aea43d5 100644
--- a/src/main/javassist/expr/Expr.java
+++ b/src/main/javassist/expr/Expr.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,6 +16,9 @@
package javassist.expr;
+import java.util.LinkedList;
+import java.util.List;
+
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtBehavior;
@@ -35,9 +39,6 @@ import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.compiler.Javac;
-import java.util.Iterator;
-import java.util.LinkedList;
-
/**
* Expression.
*/
@@ -131,7 +132,7 @@ public abstract class Expr implements Opcode {
public CtClass[] mayThrow() {
ClassPool pool = thisClass.getClassPool();
ConstPool cp = thisMethod.getConstPool();
- LinkedList list = new LinkedList();
+ List<CtClass> list = new LinkedList<CtClass>();
try {
CodeAttribute ca = thisMethod.getCodeAttribute();
ExceptionTable et = ca.getExceptionTable();
@@ -165,14 +166,12 @@ public abstract class Expr implements Opcode {
}
}
- return (CtClass[])list.toArray(new CtClass[list.size()]);
+ return list.toArray(new CtClass[list.size()]);
}
- private static void addClass(LinkedList list, CtClass c) {
- Iterator it = list.iterator();
- while (it.hasNext())
- if (it.next() == c)
- return;
+ private static void addClass(List<CtClass> list, CtClass c) {
+ if (list.contains(c))
+ return;
list.add(c);
}
@@ -188,7 +187,7 @@ public abstract class Expr implements Opcode {
/**
* Returns the line number of the source line containing the expression.
- *
+ *
* @return -1 if this information is not available.
*/
public int getLineNumber() {
@@ -204,8 +203,7 @@ public abstract class Expr implements Opcode {
ClassFile cf = thisClass.getClassFile2();
if (cf == null)
return null;
- else
- return cf.getSourceFile();
+ return cf.getSourceFile();
}
static final boolean checkResultValue(CtClass retType, String prog)
@@ -242,17 +240,15 @@ public abstract class Expr implements Opcode {
Bytecode bytecode) {
if (i >= n)
return;
- else {
- CtClass c = params[i];
- int size;
- if (c instanceof CtPrimitiveType)
- size = ((CtPrimitiveType)c).getDataSize();
- else
- size = 1;
-
- storeStack0(i + 1, n, params, regno + size, bytecode);
- bytecode.addStore(regno, c);
- }
+ CtClass c = params[i];
+ int size;
+ if (c instanceof CtPrimitiveType)
+ size = ((CtPrimitiveType)c).getDataSize();
+ else
+ size = 1;
+
+ storeStack0(i + 1, n, params, regno + size, bytecode);
+ bytecode.addStore(regno, c);
}
// The implementation of replace() should call thisClass.checkModify()
diff --git a/src/main/javassist/expr/ExprEditor.java b/src/main/javassist/expr/ExprEditor.java
index 80ddd4b..0f9bedc 100644
--- a/src/main/javassist/expr/ExprEditor.java
+++ b/src/main/javassist/expr/ExprEditor.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,14 @@
package javassist.expr;
-import javassist.bytecode.*;
-import javassist.CtClass;
import javassist.CannotCompileException;
+import javassist.CtClass;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ExceptionTable;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
/**
* A translator of method bodies.
@@ -29,7 +35,7 @@ import javassist.CannotCompileException;
* <p>If <code>instrument()</code> is called in
* <code>CtMethod</code>, the method body is scanned from the beginning
* to the end.
- * Whenever an expression, such as a method call and a <tt>new</tt>
+ * Whenever an expression, such as a method call and a <code>new</code>
* expression (object creation),
* is found, <code>edit()</code> is called in <code>ExprEdit</code>.
* <code>edit()</code> can inspect and modify the given expression.
@@ -39,7 +45,7 @@ import javassist.CannotCompileException;
*
* <p>The following code is an example:
*
- * <ul><pre>
+ * <pre>
* CtMethod cm = ...;
* cm.instrument(new ExprEditor() {
* public void edit(MethodCall m) throws CannotCompileException {
@@ -48,7 +54,7 @@ import javassist.CannotCompileException;
* + m.getLineNumber());
* }
* });
- * </pre></ul>
+ * </pre>
*
* <p>This code inspects all method calls appearing in the method represented
* by <code>cm</code> and it prints the names and the line numbers of the
@@ -245,8 +251,7 @@ public class ExprEditor {
context.updateMax(expr.locals(), expr.stack());
return true;
}
- else
- return false;
+ return false;
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
@@ -254,10 +259,10 @@ public class ExprEditor {
}
/**
- * Edits a <tt>new</tt> expression (overridable).
+ * Edits a <code>new</code> expression (overridable).
* The default implementation performs nothing.
*
- * @param e the <tt>new</tt> expression creating an object.
+ * @param e the <code>new</code> expression creating an object.
*/
public void edit(NewExpr e) throws CannotCompileException {}
@@ -265,7 +270,7 @@ public class ExprEditor {
* Edits an expression for array creation (overridable).
* The default implementation performs nothing.
*
- * @param a the <tt>new</tt> expression for creating an array.
+ * @param a the <code>new</code> expression for creating an array.
* @throws CannotCompileException
*/
public void edit(NewArray a) throws CannotCompileException {}
diff --git a/src/main/javassist/expr/FieldAccess.java b/src/main/javassist/expr/FieldAccess.java
index 56ead16..335314a 100644
--- a/src/main/javassist/expr/FieldAccess.java
+++ b/src/main/javassist/expr/FieldAccess.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,26 @@
package javassist.expr;
-import javassist.*;
-import javassist.bytecode.*;
-import javassist.compiler.*;
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtPrimitiveType;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
+import javassist.compiler.JvstCodeGen;
+import javassist.compiler.JvstTypeChecker;
+import javassist.compiler.ProceedHandler;
import javassist.compiler.ast.ASTList;
/**
@@ -36,6 +54,7 @@ public class FieldAccess extends Expr {
* Returns the method or constructor containing the field-access
* expression represented by this object.
*/
+ @Override
public CtBehavior where() { return super.where(); }
/**
@@ -44,6 +63,7 @@ public class FieldAccess extends Expr {
*
* @return -1 if this information is not available.
*/
+ @Override
public int getLineNumber() {
return super.getLineNumber();
}
@@ -53,6 +73,7 @@ public class FieldAccess extends Expr {
*
* @return null if this information is not available.
*/
+ @Override
public String getFileName() {
return super.getFileName();
}
@@ -110,7 +131,9 @@ public class FieldAccess extends Expr {
*/
public CtField getField() throws NotFoundException {
CtClass cc = getCtClass();
- return cc.getField(getFieldName());
+ int index = iterator.u16bitAt(currentPos + 1);
+ ConstPool cp = getConstPool();
+ return cc.getField(cp.getFieldrefName(index), cp.getFieldrefType(index));
}
/**
@@ -119,6 +142,7 @@ public class FieldAccess extends Expr {
* including the expression can catch and the exceptions that
* the throws declaration allows the method to throw.
*/
+ @Override
public CtClass[] mayThrow() {
return super.mayThrow();
}
@@ -146,6 +170,7 @@ public class FieldAccess extends Expr {
*
* @param statement a Java statement except try-catch.
*/
+ @Override
public void replace(String statement) throws CannotCompileException {
thisClass.getClassFile(); // to call checkModify().
ConstPool constPool = getConstPool();
@@ -233,6 +258,7 @@ public class FieldAccess extends Expr {
index = i;
}
+ @Override
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
throws CompileError
{
@@ -259,6 +285,7 @@ public class FieldAccess extends Expr {
gen.setType(fieldType);
}
+ @Override
public void setReturnType(JvstTypeChecker c, ASTList args)
throws CompileError
{
@@ -281,6 +308,7 @@ public class FieldAccess extends Expr {
index = i;
}
+ @Override
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
throws CompileError
{
@@ -311,6 +339,7 @@ public class FieldAccess extends Expr {
gen.addNullIfVoid();
}
+ @Override
public void setReturnType(JvstTypeChecker c, ASTList args)
throws CompileError
{
diff --git a/src/main/javassist/expr/Handler.java b/src/main/javassist/expr/Handler.java
index dd7e53f..1839abc 100644
--- a/src/main/javassist/expr/Handler.java
+++ b/src/main/javassist/expr/Handler.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,19 @@
package javassist.expr;
-import javassist.*;
-import javassist.bytecode.*;
-import javassist.compiler.*;
+import javassist.CannotCompileException;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.NotFoundException;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.ExceptionTable;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
/**
* A <code>catch</code> clause or a <code>finally</code> block.
@@ -40,6 +51,7 @@ public class Handler extends Expr {
/**
* Returns the method or constructor containing the catch clause.
*/
+ @Override
public CtBehavior where() { return super.where(); }
/**
@@ -47,6 +59,7 @@ public class Handler extends Expr {
*
* @return -1 if this information is not available.
*/
+ @Override
public int getLineNumber() {
return super.getLineNumber();
}
@@ -56,6 +69,7 @@ public class Handler extends Expr {
*
* @return null if this information is not available.
*/
+ @Override
public String getFileName() {
return super.getFileName();
}
@@ -63,6 +77,7 @@ public class Handler extends Expr {
/**
* Returns the list of exceptions that the catch clause may throw.
*/
+ @Override
public CtClass[] mayThrow() {
return super.mayThrow();
}
@@ -75,11 +90,9 @@ public class Handler extends Expr {
int type = etable.catchType(index);
if (type == 0)
return null;
- else {
- ConstPool cp = getConstPool();
- String name = cp.getClassInfo(type);
- return thisClass.getClassPool().getCtClass(name);
- }
+ ConstPool cp = getConstPool();
+ String name = cp.getClassInfo(type);
+ return thisClass.getClassPool().getCtClass(name);
}
/**
@@ -94,6 +107,7 @@ public class Handler extends Expr {
*
* @param statement a Java statement except try-catch.
*/
+ @Override
public void replace(String statement) throws CannotCompileException {
throw new RuntimeException("not implemented yet");
}
@@ -108,6 +122,7 @@ public class Handler extends Expr {
public void insertBefore(String src) throws CannotCompileException {
edited = true;
+ @SuppressWarnings("unused")
ConstPool cp = getConstPool();
CodeAttribute ca = iterator.get();
Javac jv = new Javac(thisClass);
diff --git a/src/main/javassist/expr/Instanceof.java b/src/main/javassist/expr/Instanceof.java
index 1ceed13..a046ddb 100644
--- a/src/main/javassist/expr/Instanceof.java
+++ b/src/main/javassist/expr/Instanceof.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,23 @@
package javassist.expr;
-import javassist.*;
-import javassist.bytecode.*;
-import javassist.compiler.*;
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
+import javassist.compiler.JvstCodeGen;
+import javassist.compiler.JvstTypeChecker;
+import javassist.compiler.ProceedHandler;
import javassist.compiler.ast.ASTList;
/**
@@ -36,6 +51,7 @@ public class Instanceof extends Expr {
* Returns the method or constructor containing the instanceof
* expression represented by this object.
*/
+ @Override
public CtBehavior where() { return super.where(); }
/**
@@ -44,6 +60,7 @@ public class Instanceof extends Expr {
*
* @return -1 if this information is not available.
*/
+ @Override
public int getLineNumber() {
return super.getLineNumber();
}
@@ -54,6 +71,7 @@ public class Instanceof extends Expr {
*
* @return null if this information is not available.
*/
+ @Override
public String getFileName() {
return super.getFileName();
}
@@ -77,6 +95,7 @@ public class Instanceof extends Expr {
* including the expression can catch and the exceptions that
* the throws declaration allows the method to throw.
*/
+ @Override
public CtClass[] mayThrow() {
return super.mayThrow();
}
@@ -89,8 +108,10 @@ public class Instanceof extends Expr {
*
* @param statement a Java statement except try-catch.
*/
+ @Override
public void replace(String statement) throws CannotCompileException {
thisClass.getClassFile(); // to call checkModify().
+ @SuppressWarnings("unused")
ConstPool constPool = getConstPool();
int pos = currentPos;
int index = iterator.u16bitAt(pos + 1);
@@ -145,6 +166,7 @@ public class Instanceof extends Expr {
index = i;
}
+ @Override
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
throws CompileError
{
@@ -159,6 +181,7 @@ public class Instanceof extends Expr {
gen.setType(CtClass.booleanType);
}
+ @Override
public void setReturnType(JvstTypeChecker c, ASTList args)
throws CompileError
{
diff --git a/src/main/javassist/expr/MethodCall.java b/src/main/javassist/expr/MethodCall.java
index 9e9d1db..66ddead 100644
--- a/src/main/javassist/expr/MethodCall.java
+++ b/src/main/javassist/expr/MethodCall.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,21 @@
package javassist.expr;
-import javassist.*;
-import javassist.bytecode.*;
-import javassist.compiler.*;
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.MethodInfo;
+import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
/**
* Method invocation (caller-side expression).
@@ -38,14 +51,14 @@ public class MethodCall extends Expr {
if (c == INVOKEINTERFACE)
return cp.getInterfaceMethodrefNameAndType(index);
- else
- return cp.getMethodrefNameAndType(index);
+ return cp.getMethodrefNameAndType(index);
}
/**
* Returns the method or constructor containing the method-call
* expression represented by this object.
*/
+ @Override
public CtBehavior where() { return super.where(); }
/**
@@ -54,6 +67,7 @@ public class MethodCall extends Expr {
*
* @return -1 if this information is not available.
*/
+ @Override
public int getLineNumber() {
return super.getLineNumber();
}
@@ -63,6 +77,7 @@ public class MethodCall extends Expr {
*
* @return null if this information is not available.
*/
+ @Override
public String getFileName() {
return super.getFileName();
}
@@ -136,6 +151,7 @@ public class MethodCall extends Expr {
* including the expression can catch and the exceptions that
* the throws declaration allows the method to throw.
*/
+ @Override
public CtClass[] mayThrow() {
return super.mayThrow();
}
@@ -175,6 +191,7 @@ public class MethodCall extends Expr {
*
* @param statement a Java statement except try-catch.
*/
+ @Override
public void replace(String statement) throws CannotCompileException {
thisClass.getClassFile(); // to call checkModify().
ConstPool constPool = getConstPool();
@@ -214,7 +231,7 @@ public class MethodCall extends Expr {
jc.recordStaticProceed(classname, methodname);
else if (c == INVOKESPECIAL)
jc.recordSpecialProceed(Javac.param0Name, classname,
- methodname, signature);
+ methodname, signature, index);
else
jc.recordProceed(Javac.param0Name, methodname);
diff --git a/src/main/javassist/expr/NewArray.java b/src/main/javassist/expr/NewArray.java
index c5ac41e..df30e26 100644
--- a/src/main/javassist/expr/NewArray.java
+++ b/src/main/javassist/expr/NewArray.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,24 @@
package javassist.expr;
-import javassist.*;
-import javassist.bytecode.*;
-import javassist.compiler.*;
+import javassist.CannotCompileException;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.CtPrimitiveType;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
+import javassist.compiler.JvstCodeGen;
+import javassist.compiler.JvstTypeChecker;
+import javassist.compiler.ProceedHandler;
import javassist.compiler.ast.ASTList;
/**
@@ -39,6 +55,7 @@ public class NewArray extends Expr {
* Returns the method or constructor containing the array creation
* represented by this object.
*/
+ @Override
public CtBehavior where() { return super.where(); }
/**
@@ -47,6 +64,7 @@ public class NewArray extends Expr {
*
* @return -1 if this information is not available.
*/
+ @Override
public int getLineNumber() {
return super.getLineNumber();
}
@@ -56,6 +74,7 @@ public class NewArray extends Expr {
*
* @return null if this information is not available.
*/
+ @Override
public String getFileName() {
return super.getFileName();
}
@@ -66,15 +85,16 @@ public class NewArray extends Expr {
* including the expression can catch and the exceptions that
* the throws declaration allows the method to throw.
*/
+ @Override
public CtClass[] mayThrow() {
return super.mayThrow();
}
/**
* Returns the type of array components. If the created array is
- * a two-dimensional array of <tt>int</tt>,
+ * a two-dimensional array of <code>int</code>,
* the type returned by this method is
- * not <tt>int[]</tt> but <tt>int</tt>.
+ * not <code>int[]</code> but <code>int</code>.
*/
public CtClass getComponentType() throws NotFoundException {
if (opcode == Opcode.NEWARRAY) {
@@ -141,8 +161,7 @@ public class NewArray extends Expr {
public int getCreatedDimensions() {
if (opcode == Opcode.MULTIANEWARRAY)
return iterator.byteAt(currentPos + 3);
- else
- return 1;
+ return 1;
}
/**
@@ -155,6 +174,7 @@ public class NewArray extends Expr {
*
* @param statement a Java statement except try-catch.
*/
+ @Override
public void replace(String statement) throws CannotCompileException {
try {
replace2(statement);
@@ -249,10 +269,11 @@ public class NewArray extends Expr {
dimension = dim;
}
+ @Override
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
throws CompileError
{
- int num = gen.getMethodArgsLength(args);
+ int num = gen.getMethodArgsLength(args);
if (num != dimension)
throw new CompileError(Javac.proceedName
+ "() with a wrong number of parameters");
@@ -273,6 +294,7 @@ public class NewArray extends Expr {
gen.setType(arrayType);
}
+ @Override
public void setReturnType(JvstTypeChecker c, ASTList args)
throws CompileError
{
diff --git a/src/main/javassist/expr/NewExpr.java b/src/main/javassist/expr/NewExpr.java
index c2ab044..3171fc3 100644
--- a/src/main/javassist/expr/NewExpr.java
+++ b/src/main/javassist/expr/NewExpr.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,13 +16,29 @@
package javassist.expr;
-import javassist.*;
-import javassist.bytecode.*;
-import javassist.compiler.*;
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
+import javassist.compiler.JvstCodeGen;
+import javassist.compiler.JvstTypeChecker;
+import javassist.compiler.ProceedHandler;
import javassist.compiler.ast.ASTList;
/**
- * Object creation (<tt>new</tt> expression).
+ * Object creation (<code>new</code> expression).
*/
public class NewExpr extends Expr {
String newTypeName;
@@ -52,26 +69,29 @@ public class NewExpr extends Expr {
} */
/**
- * Returns the method or constructor containing the <tt>new</tt>
+ * Returns the method or constructor containing the <code>new</code>
* expression represented by this object.
*/
+ @Override
public CtBehavior where() { return super.where(); }
/**
* Returns the line number of the source line containing the
- * <tt>new</tt> expression.
+ * <code>new</code> expression.
*
* @return -1 if this information is not available.
*/
+ @Override
public int getLineNumber() {
return super.getLineNumber();
}
/**
- * Returns the source file containing the <tt>new</tt> expression.
+ * Returns the source file containing the <code>new</code> expression.
*
* @return null if this information is not available.
*/
+ @Override
public String getFileName() {
return super.getFileName();
}
@@ -122,6 +142,7 @@ public class NewExpr extends Expr {
* including the expression can catch and the exceptions that
* the throws declaration allows the method to throw.
*/
+ @Override
public CtClass[] mayThrow() {
return super.mayThrow();
}
@@ -139,8 +160,9 @@ public class NewExpr extends Expr {
private int canReplace() throws CannotCompileException {
int op = iterator.byteAt(newPos + 3);
- if (op == Opcode.DUP)
- return 4;
+ if (op == Opcode.DUP) // Typical single DUP or Javaflow DUP DUP2_X2 POP2
+ return ((iterator.byteAt(newPos + 4) == Opcode.DUP2_X2
+ && iterator.byteAt(newPos + 5) == Opcode.POP2)) ? 6 : 4;
else if (op == Opcode.DUP_X1
&& iterator.byteAt(newPos + 4) == Opcode.SWAP)
return 5;
@@ -151,13 +173,14 @@ public class NewExpr extends Expr {
}
/**
- * Replaces the <tt>new</tt> expression with the bytecode derived from
+ * Replaces the <code>new</code> expression with the bytecode derived from
* the given source text.
*
* <p>$0 is available but the value is null.
*
* @param statement a Java statement except try-catch.
*/
+ @Override
public void replace(String statement) throws CannotCompileException {
thisClass.getClassFile(); // to call checkModify().
@@ -226,6 +249,7 @@ public class NewExpr extends Expr {
methodIndex = mi;
}
+ @Override
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
throws CompileError
{
@@ -237,6 +261,7 @@ public class NewExpr extends Expr {
gen.setType(newType);
}
+ @Override
public void setReturnType(JvstTypeChecker c, ASTList args)
throws CompileError
{
diff --git a/src/main/javassist/package.html b/src/main/javassist/package.html
index f5b66b9..f67c858 100644
--- a/src/main/javassist/package.html
+++ b/src/main/javassist/package.html
@@ -12,9 +12,9 @@ See the description of this class first.
<p>To know the version number of this package, type the following command:
-<ul><pre>
+<pre>
java -jar javassist.jar
-</pre></ul>
+</pre>
<p>It prints the version number on the console.
diff --git a/src/main/javassist/runtime/Cflow.java b/src/main/javassist/runtime/Cflow.java
index 641c63f..0564747 100644
--- a/src/main/javassist/runtime/Cflow.java
+++ b/src/main/javassist/runtime/Cflow.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -22,31 +23,32 @@ package javassist.runtime;
*
* @see javassist.CtBehavior#useCflow(String)
*/
-public class Cflow extends ThreadLocal {
- private static class Depth {
+public class Cflow extends ThreadLocal<Cflow.Depth> {
+ protected static class Depth {
private int depth;
Depth() { depth = 0; }
- int get() { return depth; }
+ int value() { return depth; }
void inc() { ++depth; }
void dec() { --depth; }
}
- protected synchronized Object initialValue() {
+ @Override
+ protected synchronized Depth initialValue() {
return new Depth();
}
/**
* Increments the counter.
*/
- public void enter() { ((Depth)get()).inc(); }
+ public void enter() { get().inc(); }
/**
* Decrements the counter.
*/
- public void exit() { ((Depth)get()).dec(); }
+ public void exit() { get().dec(); }
/**
* Returns the value of the counter.
*/
- public int value() { return ((Depth)get()).get(); }
+ public int value() { return get().value(); }
}
diff --git a/src/main/javassist/runtime/Desc.java b/src/main/javassist/runtime/Desc.java
index fa86a74..c733b30 100644
--- a/src/main/javassist/runtime/Desc.java
+++ b/src/main/javassist/runtime/Desc.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -27,27 +28,25 @@ public class Desc {
* Specifies how a <code>java.lang.Class</code> object is loaded.
*
* <p>If true, it is loaded by:
- * <ul><pre>Thread.currentThread().getContextClassLoader().loadClass()</pre></ul>
+ * <pre>Thread.currentThread().getContextClassLoader().loadClass()</pre>
* <p>If false, it is loaded by <code>Class.forName()</code>.
* The default value is false.
*/
public static boolean useContextClassLoader = false;
- private static Class getClassObject(String name)
+ private static Class<?> getClassObject(String name)
throws ClassNotFoundException
{
if (useContextClassLoader)
- return Thread.currentThread().getContextClassLoader()
- .loadClass(name);
- else
- return Class.forName(name);
+ return Class.forName(name, true, Thread.currentThread().getContextClassLoader());
+ return Class.forName(name);
}
/**
* Interprets the given class name.
* It is used for implementing <code>$class</code>.
*/
- public static Class getClazz(String name) {
+ public static Class<?> getClazz(String name) {
try {
return getClassObject(name);
}
@@ -63,7 +62,7 @@ public class Desc {
* Interprets the given type descriptor representing a method
* signature. It is used for implementing <code>$sig</code>.
*/
- public static Class[] getParams(String desc) {
+ public static Class<?>[] getParams(String desc) {
if (desc.charAt(0) != '(')
throw new RuntimeException("$sig: internal error");
@@ -74,17 +73,17 @@ public class Desc {
* Interprets the given type descriptor.
* It is used for implementing <code>$type</code>.
*/
- public static Class getType(String desc) {
- Class[] result = getType(desc, desc.length(), 0, 0);
+ public static Class<?> getType(String desc) {
+ Class<?>[] result = getType(desc, desc.length(), 0, 0);
if (result == null || result.length != 1)
throw new RuntimeException("$type: internal error");
return result[0];
}
- private static Class[] getType(String desc, int descLen,
+ private static Class<?>[] getType(String desc, int descLen,
int start, int num) {
- Class clazz;
+ Class<?> clazz;
if (start >= descLen)
return new Class[num];
@@ -124,12 +123,12 @@ public class Desc {
return new Class[num];
}
- Class[] result = getType(desc, descLen, start + 1, num + 1);
+ Class<?>[] result = getType(desc, descLen, start + 1, num + 1);
result[num] = clazz;
return result;
}
- private static Class[] getClassType(String desc, int descLen,
+ private static Class<?>[] getClassType(String desc, int descLen,
int start, int num) {
int end = start;
while (desc.charAt(end) == '[')
@@ -147,7 +146,7 @@ public class Desc {
else
cname = desc.substring(start, end + 1);
- Class[] result = getType(desc, descLen, end + 1, num + 1);
+ Class<?>[] result = getType(desc, descLen, end + 1, num + 1);
try {
result[num] = getClassObject(cname.replace('/', '.'));
}
diff --git a/src/main/javassist/runtime/DotClass.java b/src/main/javassist/runtime/DotClass.java
index 29db811..a1a974e 100644
--- a/src/main/javassist/runtime/DotClass.java
+++ b/src/main/javassist/runtime/DotClass.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/runtime/Inner.java b/src/main/javassist/runtime/Inner.java
index ef96c50..697c816 100644
--- a/src/main/javassist/runtime/Inner.java
+++ b/src/main/javassist/runtime/Inner.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/scopedpool/ScopedClassPool.java b/src/main/javassist/scopedpool/ScopedClassPool.java
index 4833773..63fc9a7 100644
--- a/src/main/javassist/scopedpool/ScopedClassPool.java
+++ b/src/main/javassist/scopedpool/ScopedClassPool.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,9 +16,9 @@
package javassist.scopedpool;
+import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.security.ProtectionDomain;
-import java.util.Iterator;
import java.util.Map;
import javassist.CannotCompileException;
import javassist.ClassPool;
@@ -36,11 +37,11 @@ import javassist.NotFoundException;
public class ScopedClassPool extends ClassPool {
protected ScopedClassPoolRepository repository;
- protected WeakReference classLoader;
+ protected Reference<ClassLoader> classLoader;
protected LoaderClassPath classPath;
- protected SoftValueHashMap softcache = new SoftValueHashMap();
+ protected Map<String,CtClass> softcache = new SoftValueHashMap<String,CtClass>();
boolean isBootstrapCl = true;
@@ -58,10 +59,10 @@ public class ScopedClassPool extends ClassPool {
* the original class pool
* @param repository
* the repository
- *@deprecated
*/
protected ScopedClassPool(ClassLoader cl, ClassPool src,
- ScopedClassPoolRepository repository) {
+ ScopedClassPoolRepository repository)
+ {
this(cl, src, repository, false);
}
@@ -77,11 +78,12 @@ public class ScopedClassPool extends ClassPool {
* @param isTemp
* Whether this is a temporary pool used to resolve references
*/
- protected ScopedClassPool(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository, boolean isTemp)
+ protected ScopedClassPool(ClassLoader cl, ClassPool src,
+ ScopedClassPoolRepository repository, boolean isTemp)
{
super(src);
this.repository = repository;
- this.classLoader = new WeakReference(cl);
+ this.classLoader = new WeakReference<ClassLoader>(cl);
if (cl != null) {
classPath = new LoaderClassPath(cl);
this.insertClassPath(classPath);
@@ -109,7 +111,7 @@ public class ScopedClassPool extends ClassPool {
}
protected ClassLoader getClassLoader0() {
- return (ClassLoader)classLoader.get();
+ return classLoader.get();
}
/**
@@ -117,7 +119,6 @@ public class ScopedClassPool extends ClassPool {
*/
public void close() {
this.removeClassPath(classPath);
- classPath.close();
classes.clear();
softcache.clear();
}
@@ -162,6 +163,7 @@ public class ScopedClassPool extends ClassPool {
* the class name
* @return the class
*/
+ @Override
protected CtClass getCached(String classname) {
CtClass clazz = getCachedLocally(classname);
if (clazz == null) {
@@ -185,11 +187,9 @@ public class ScopedClassPool extends ClassPool {
}
if (!isLocal) {
- Map registeredCLs = repository.getRegisteredCLs();
+ Map<ClassLoader,ScopedClassPool> registeredCLs = repository.getRegisteredCLs();
synchronized (registeredCLs) {
- Iterator it = registeredCLs.values().iterator();
- while (it.hasNext()) {
- ScopedClassPool pool = (ScopedClassPool)it.next();
+ for (ScopedClassPool pool:registeredCLs.values()) {
if (pool.isUnloadedClassLoader()) {
repository.unregisterClassLoader(pool
.getClassLoader());
@@ -288,7 +288,7 @@ public class ScopedClassPool extends ClassPool {
* @throws CannotCompileException
* for any error
*/
- public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain)
+ public Class<?> toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain)
throws CannotCompileException {
// We need to pass up the classloader stored in this pool, as the
// default implementation uses the Thread context cl.
diff --git a/src/main/javassist/scopedpool/ScopedClassPoolFactory.java b/src/main/javassist/scopedpool/ScopedClassPoolFactory.java
index 1a998a9..419b7ed 100644
--- a/src/main/javassist/scopedpool/ScopedClassPoolFactory.java
+++ b/src/main/javassist/scopedpool/ScopedClassPoolFactory.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/scopedpool/ScopedClassPoolFactoryImpl.java b/src/main/javassist/scopedpool/ScopedClassPoolFactoryImpl.java
index b8e66d6..f2b0a48 100644
--- a/src/main/javassist/scopedpool/ScopedClassPoolFactoryImpl.java
+++ b/src/main/javassist/scopedpool/ScopedClassPoolFactoryImpl.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/scopedpool/ScopedClassPoolRepository.java b/src/main/javassist/scopedpool/ScopedClassPoolRepository.java
index 7ebf8f8..33e4712 100644
--- a/src/main/javassist/scopedpool/ScopedClassPoolRepository.java
+++ b/src/main/javassist/scopedpool/ScopedClassPoolRepository.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -80,7 +81,7 @@ public interface ScopedClassPoolRepository {
*
* @return the registered classloaders.
*/
- Map getRegisteredCLs();
+ Map<ClassLoader,ScopedClassPool> getRegisteredCLs();
/**
* This method will check to see if a register classloader has been
diff --git a/src/main/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java b/src/main/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java
index 2245c7d..8c20d97 100644
--- a/src/main/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java
+++ b/src/main/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -17,7 +18,7 @@ package javassist.scopedpool;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@@ -42,8 +43,8 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
boolean pruneWhenCached;
/** The registered classloaders */
- protected Map registeredCLs = Collections
- .synchronizedMap(new WeakHashMap());
+ protected Map<ClassLoader,ScopedClassPool> registeredCLs = Collections
+ .synchronizedMap(new WeakHashMap<ClassLoader,ScopedClassPool>());
/** The default class pool */
protected ClassPool classpool;
@@ -75,6 +76,7 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
*
* @return the prune.
*/
+ @Override
public boolean isPrune() {
return prune;
}
@@ -84,6 +86,7 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
*
* @param prune a new value.
*/
+ @Override
public void setPrune(boolean prune) {
this.prune = prune;
}
@@ -95,10 +98,12 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
* @param src the original classpool.
* @return the classpool
*/
+ @Override
public ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src) {
return factory.create(cl, src, this);
}
+ @Override
public ClassPool findClassPool(ClassLoader cl) {
if (cl == null)
return registerClassLoader(ClassLoader.getSystemClassLoader());
@@ -112,6 +117,7 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
* @param ucl the classloader.
* @return the classpool
*/
+ @Override
public ClassPool registerClassLoader(ClassLoader ucl) {
synchronized (registeredCLs) {
// FIXME: Probably want to take this method out later
@@ -120,7 +126,7 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
// a
// ClassPool.classpath
if (registeredCLs.containsKey(ucl)) {
- return (ClassPool)registeredCLs.get(ucl);
+ return registeredCLs.get(ucl);
}
ScopedClassPool pool = createScopedClassPool(ucl, classpool);
registeredCLs.put(ucl, pool);
@@ -131,7 +137,8 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
/**
* Get the registered classloaders.
*/
- public Map getRegisteredCLs() {
+ @Override
+ public Map<ClassLoader,ScopedClassPool> getRegisteredCLs() {
clearUnregisteredClassLoaders();
return registeredCLs;
}
@@ -140,34 +147,31 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
* This method will check to see if a register classloader has been
* undeployed (as in JBoss)
*/
+ @Override
public void clearUnregisteredClassLoaders() {
- ArrayList toUnregister = null;
+ List<ClassLoader> toUnregister = null;
synchronized (registeredCLs) {
- Iterator it = registeredCLs.values().iterator();
- while (it.hasNext()) {
- ScopedClassPool pool = (ScopedClassPool)it.next();
- if (pool.isUnloadedClassLoader()) {
- it.remove();
- ClassLoader cl = pool.getClassLoader();
+ for (Map.Entry<ClassLoader,ScopedClassPool> reg:registeredCLs.entrySet()) {
+ if (reg.getValue().isUnloadedClassLoader()) {
+ ClassLoader cl = reg.getValue().getClassLoader();
if (cl != null) {
- if (toUnregister == null) {
- toUnregister = new ArrayList();
- }
+ if (toUnregister == null)
+ toUnregister = new ArrayList<ClassLoader>();
toUnregister.add(cl);
}
+ registeredCLs.remove(reg.getKey());
}
}
- if (toUnregister != null) {
- for (int i = 0; i < toUnregister.size(); i++) {
- unregisterClassLoader((ClassLoader)toUnregister.get(i));
- }
- }
+ if (toUnregister != null)
+ for (ClassLoader cl:toUnregister)
+ unregisterClassLoader(cl);
}
}
+ @Override
public void unregisterClassLoader(ClassLoader cl) {
synchronized (registeredCLs) {
- ScopedClassPool pool = (ScopedClassPool)registeredCLs.remove(cl);
+ ScopedClassPool pool = registeredCLs.remove(cl);
if (pool != null)
pool.close();
}
@@ -177,10 +181,12 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
// Noop - this is the end
}
+ @Override
public void setClassPoolFactory(ScopedClassPoolFactory factory) {
this.factory = factory;
}
+ @Override
public ScopedClassPoolFactory getClassPoolFactory() {
return factory;
}
diff --git a/src/main/javassist/scopedpool/SoftValueHashMap.java b/src/main/javassist/scopedpool/SoftValueHashMap.java
index 6a73d9e..01d8498 100644
--- a/src/main/javassist/scopedpool/SoftValueHashMap.java
+++ b/src/main/javassist/scopedpool/SoftValueHashMap.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -17,33 +18,37 @@ package javassist.scopedpool;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
-import java.util.AbstractMap;
-import java.util.HashMap;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* This Map will remove entries when the value in the map has been cleaned from
* garbage collection
*
- * @version <tt>$Revision: 1.4 $</tt>
+ * @version <code>$Revision: 1.4 $</code>
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
*/
-public class SoftValueHashMap extends AbstractMap implements Map {
- private static class SoftValueRef extends SoftReference {
- public Object key;
+public class SoftValueHashMap<K,V> implements Map<K,V> {
+ private static class SoftValueRef<K,V> extends SoftReference<V> {
+ public K key;
- private SoftValueRef(Object key, Object val, ReferenceQueue q) {
+ private SoftValueRef(K key, V val, ReferenceQueue<V> q) {
super(val, q);
this.key = key;
}
- private static SoftValueRef create(Object key, Object val,
- ReferenceQueue q) {
+ private static <K,V> SoftValueRef<K,V> create(
+ K key, V val, ReferenceQueue<V> q) {
if (val == null)
return null;
else
- return new SoftValueRef(key, val, q);
+ return new SoftValueRef<K,V>(key, val, q);
}
}
@@ -51,30 +56,37 @@ public class SoftValueHashMap extends AbstractMap implements Map {
/**
* Returns a set of the mappings contained in this hash table.
*/
- public Set entrySet() {
+ @Override
+ public Set<Map.Entry<K, V>> entrySet() {
processQueue();
- return hash.entrySet();
+ Set<Entry<K,V>> ret = new HashSet<Entry<K,V>>();
+ for (Entry<K,SoftValueRef<K,V>> e:hash.entrySet())
+ ret.add(new SimpleImmutableEntry<K,V> (
+ e.getKey(), e.getValue().get()));
+ return ret;
}
/* Hash table mapping WeakKeys to values */
- private Map hash;
+ private Map<K,SoftValueRef<K,V>> hash;
/* Reference queue for cleared WeakKeys */
- private ReferenceQueue queue = new ReferenceQueue();
+ private ReferenceQueue<V> queue = new ReferenceQueue<V>();
/*
* Remove all invalidated entries from the map, that is, remove all entries
* whose values have been discarded.
*/
private void processQueue() {
- SoftValueRef ref;
- while ((ref = (SoftValueRef)queue.poll()) != null) {
- if (ref == (SoftValueRef)hash.get(ref.key)) {
- // only remove if it is the *exact* same WeakValueRef
- //
- hash.remove(ref.key);
+ Object ref;
+ if (!hash.isEmpty())
+ while ((ref = queue.poll()) != null)
+ if (ref instanceof SoftValueRef) {
+ @SuppressWarnings("rawtypes")
+ SoftValueRef que =(SoftValueRef) ref;
+ if (ref == hash.get(que.key))
+ // only remove if it is the *exact* same SoftValueRef
+ hash.remove(que.key);
}
- }
}
/* -- Constructors -- */
@@ -94,7 +106,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
* factor is nonpositive
*/
public SoftValueHashMap(int initialCapacity, float loadFactor) {
- hash = new HashMap(initialCapacity, loadFactor);
+ hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>(initialCapacity, loadFactor);
}
/**
@@ -108,7 +120,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
* If the initial capacity is less than zero
*/
public SoftValueHashMap(int initialCapacity) {
- hash = new HashMap(initialCapacity);
+ hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>(initialCapacity);
}
/**
@@ -116,19 +128,19 @@ public class SoftValueHashMap extends AbstractMap implements Map {
* initial capacity and the default load factor, which is <code>0.75</code>.
*/
public SoftValueHashMap() {
- hash = new HashMap();
+ hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>();
}
/**
* Constructs a new <code>WeakHashMap</code> with the same mappings as the
- * specified <tt>Map</tt>. The <code>WeakHashMap</code> is created with
+ * specified <code>Map</code>. The <code>WeakHashMap</code> is created with
* an initial capacity of twice the number of mappings in the specified map
* or 11 (whichever is greater), and a default load factor, which is
- * <tt>0.75</tt>.
+ * <code>0.75</code>.
*
* @param t the map whose mappings are to be placed in this map.
*/
- public SoftValueHashMap(Map t) {
+ public SoftValueHashMap(Map<K,V> t) {
this(Math.max(2 * t.size(), 11), 0.75f);
putAll(t);
}
@@ -141,6 +153,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
* <code>Map</code> interface, the time required by this operation is
* linear in the size of the map.</em>
*/
+ @Override
public int size() {
processQueue();
return hash.size();
@@ -149,6 +162,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
/**
* Returns <code>true</code> if this map contains no key-value mappings.
*/
+ @Override
public boolean isEmpty() {
processQueue();
return hash.isEmpty();
@@ -161,6 +175,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
* @param key
* The key whose presence in this map is to be tested.
*/
+ @Override
public boolean containsKey(Object key) {
processQueue();
return hash.containsKey(key);
@@ -176,12 +191,10 @@ public class SoftValueHashMap extends AbstractMap implements Map {
* @param key
* The key whose associated value, if any, is to be returned.
*/
- public Object get(Object key) {
+ @Override
+ public V get(Object key) {
processQueue();
- SoftReference ref = (SoftReference)hash.get(key);
- if (ref != null)
- return ref.get();
- return null;
+ return valueOrNull(hash.get(key));
}
/**
@@ -199,12 +212,10 @@ public class SoftValueHashMap extends AbstractMap implements Map {
* @return The previous value to which this key was mapped, or
* <code>null</code> if if there was no mapping for the key
*/
- public Object put(Object key, Object value) {
+ @Override
+ public V put(K key, V value) {
processQueue();
- Object rtn = hash.put(key, SoftValueRef.create(key, value, queue));
- if (rtn != null)
- rtn = ((SoftReference)rtn).get();
- return rtn;
+ return valueOrNull(hash.put(key, SoftValueRef.create(key, value, queue)));
}
/**
@@ -217,16 +228,66 @@ public class SoftValueHashMap extends AbstractMap implements Map {
* @return The value to which this key was mapped, or <code>null</code> if
* there was no mapping for the key.
*/
- public Object remove(Object key) {
+ @Override
+ public V remove(Object key) {
processQueue();
- return hash.remove(key);
+ return valueOrNull(hash.remove(key));
}
/**
* Removes all mappings from this map.
*/
+ @Override
public void clear() {
processQueue();
hash.clear();
}
+
+ /*
+ * Check whether the supplied value exists.
+ * @param Object the value to compare.
+ * @return true if it was found or null.
+ */
+ @Override
+ public boolean containsValue(Object arg0) {
+ processQueue();
+ if (null == arg0)
+ return false;
+
+ for (SoftValueRef<K,V> e:hash.values())
+ if (null != e && arg0.equals(e.get()))
+ return true;
+ return false;
+ }
+
+ /* {@inheritDoc} */
+ @Override
+ public Set<K> keySet() {
+ processQueue();
+ return hash.keySet();
+ }
+
+ /* {@inheritDoc} */
+ @Override
+ public void putAll(Map<? extends K,? extends V> arg0) {
+ processQueue();
+ for (K key:arg0.keySet())
+ put(key, arg0.get(key));
+ }
+
+ /* {@inheritDoc} */
+ @Override
+ public Collection<V> values() {
+ processQueue();
+ List<V> ret = new ArrayList<V>();
+ for (SoftValueRef<K,V> e:hash.values())
+ ret.add(e.get());
+ return ret;
+ }
+
+ private V valueOrNull(SoftValueRef<K,V> rtn) {
+ if (null == rtn)
+ return null;
+ return rtn.get();
+ }
}
diff --git a/src/main/javassist/tools/Callback.java b/src/main/javassist/tools/Callback.java
new file mode 100644
index 0000000..c0c8809
--- /dev/null
+++ b/src/main/javassist/tools/Callback.java
@@ -0,0 +1,154 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+
+package javassist.tools;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import javassist.CannotCompileException;
+import javassist.CtBehavior;
+
+/**
+ * Creates bytecode that when executed calls back to the instance's result method.
+ *
+ * <p>Example of how to create and insert a callback:</p>
+ * <pre>
+ * ctMethod.insertAfter(new Callback("Thread.currentThread()") {
+ * public void result(Object[] objects) {
+ * Thread thread = (Thread) objects[0];
+ * // do something with thread...
+ * }
+ * }.sourceCode());
+ * </pre>
+ * <p>Contains utility methods for inserts callbacks in <code>CtBehaviour</code>, example:</p>
+ * <pre>
+ * insertAfter(ctBehaviour, new Callback("Thread.currentThread(), dummyString") {
+ * public void result(Object[] objects) {
+ * Thread thread = (Thread) objects[0];
+ * // do something with thread...
+ * }
+ * });
+ * </pre>
+ *
+ * @author Marten Hedborg
+ * @author Shigeru Chiba
+ */
+public abstract class Callback {
+
+ public static Map<String,Callback> callbacks = new HashMap<String,Callback>();
+
+ private final String sourceCode;
+
+ /**
+ * Constructs a new <code>Callback</code> object.
+ *
+ * @param src The source code representing the inserted callback bytecode.
+ * Can be one or many single statements each returning one object.
+ * If many single statements are used they must be comma separated.
+ */
+ public Callback(String src){
+ String uuid = UUID.randomUUID().toString();
+ callbacks.put(uuid, this);
+ sourceCode = "((javassist.tools.Callback) javassist.tools.Callback.callbacks.get(\""+uuid+"\")).result(new Object[]{"+src+"});";
+ }
+
+ /**
+ * Gets called when bytecode is executed
+ *
+ * @param objects Objects that the bytecode in callback returns
+ */
+ public abstract void result(Object[] objects);
+
+ @Override
+ public String toString(){
+ return sourceCode();
+ }
+
+ public String sourceCode(){
+ return sourceCode;
+ }
+
+ /**
+ * Utility method to insert callback at the beginning of the body.
+ *
+ * @param callback The callback
+ *
+ * @see CtBehavior#insertBefore(String)
+ */
+ public static void insertBefore(CtBehavior behavior, Callback callback)
+ throws CannotCompileException
+ {
+ behavior.insertBefore(callback.toString());
+ }
+
+ /**
+ * Utility method to inserts callback at the end of the body.
+ * The callback is inserted just before every return instruction.
+ * It is not executed when an exception is thrown.
+ *
+ * @param behavior The behaviour to insert callback in
+ * @param callback The callback
+ *
+ * @see CtBehavior#insertAfter(String, boolean)
+ */
+ public static void insertAfter(CtBehavior behavior,Callback callback)
+ throws CannotCompileException
+ {
+ behavior.insertAfter(callback.toString(), false);
+ }
+
+ /**
+ * Utility method to inserts callback at the end of the body.
+ * The callback is inserted just before every return instruction.
+ * It is not executed when an exception is thrown.
+ *
+ * @param behavior The behaviour to insert callback in
+ * @param callback The callback representing the inserted.
+ * @param asFinally True if the inserted is executed
+ * Not only when the control normally returns
+ * but also when an exception is thrown.
+ * If this parameter is true, the inserted code cannot
+ * access local variables.
+ *
+ * @see CtBehavior#insertAfter(String, boolean)
+ */
+ public static void insertAfter(CtBehavior behavior, Callback callback, boolean asFinally)
+ throws CannotCompileException
+ {
+ behavior.insertAfter(callback.toString(), asFinally);
+ }
+
+ /**
+ * Utility method to inserts callback at the specified line in the body.
+ *
+ * @param behavior The behaviour to insert callback in
+ * @param callback The callback representing.
+ * @param lineNum The line number. The callback is inserted at the
+ * beginning of the code at the line specified by this
+ * line number.
+ *
+ * @return The line number at which the callback has been inserted.
+ *
+ * @see CtBehavior#insertAt(int, String)
+ */
+ public static int insertAt(CtBehavior behavior, Callback callback, int lineNum)
+ throws CannotCompileException
+ {
+ return behavior.insertAt(lineNum, callback.toString());
+ }
+}
diff --git a/src/main/javassist/tools/Dump.java b/src/main/javassist/tools/Dump.java
index 2f064a8..d672271 100644
--- a/src/main/javassist/tools/Dump.java
+++ b/src/main/javassist/tools/Dump.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,7 +16,10 @@
package javassist.tools;
-import java.io.*;
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.io.PrintWriter;
+
import javassist.bytecode.ClassFile;
import javassist.bytecode.ClassFilePrinter;
@@ -25,7 +29,7 @@ import javassist.bytecode.ClassFilePrinter;
* the class file is broken.
*
* <p>For example,
- * <ul><pre>% java javassist.tools.Dump foo.class</pre></ul>
+ * <pre>% java javassist.tools.Dump foo.class</pre>
*
* <p>prints the contents of the constant pool and the list of methods
* and fields.
diff --git a/src/main/javassist/tools/framedump.java b/src/main/javassist/tools/framedump.java
index 0a30fb1..57c21c8 100644
--- a/src/main/javassist/tools/framedump.java
+++ b/src/main/javassist/tools/framedump.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -23,7 +24,7 @@ import javassist.bytecode.analysis.FramePrinter;
* of all methods in a class.
*
* <p>For example,
- * <ul><pre>% java javassist.tools.framedump foo.class</pre></ul>
+ * <pre>% java javassist.tools.framedump foo.class</pre>
*/
public class framedump {
private framedump() {}
@@ -35,7 +36,7 @@ public class framedump {
*/
public static void main(String[] args) throws Exception {
if (args.length != 1) {
- System.err.println("Usage: java javassist.tools.framedump <class file name>");
+ System.err.println("Usage: java javassist.tools.framedump <fully-qualified class name>");
return;
}
diff --git a/src/main/javassist/tools/reflect/CannotCreateException.java b/src/main/javassist/tools/reflect/CannotCreateException.java
index 75ffe0c..83f92fc 100644
--- a/src/main/javassist/tools/reflect/CannotCreateException.java
+++ b/src/main/javassist/tools/reflect/CannotCreateException.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -19,6 +20,9 @@ package javassist.tools.reflect;
* Signals that <code>ClassMetaobject.newInstance()</code> fails.
*/
public class CannotCreateException extends Exception {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public CannotCreateException(String s) {
super(s);
}
diff --git a/src/main/javassist/tools/reflect/CannotInvokeException.java b/src/main/javassist/tools/reflect/CannotInvokeException.java
index 8c063d7..a9d0524 100644
--- a/src/main/javassist/tools/reflect/CannotInvokeException.java
+++ b/src/main/javassist/tools/reflect/CannotInvokeException.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,7 +17,6 @@
package javassist.tools.reflect;
import java.lang.reflect.InvocationTargetException;
-import java.lang.IllegalAccessException;
/**
* Thrown when method invocation using the reflection API has thrown
@@ -28,6 +28,8 @@ import java.lang.IllegalAccessException;
*/
public class CannotInvokeException extends RuntimeException {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private Throwable err = null;
/**
diff --git a/src/main/javassist/tools/reflect/CannotReflectException.java b/src/main/javassist/tools/reflect/CannotReflectException.java
index 0af2892..dc90710 100644
--- a/src/main/javassist/tools/reflect/CannotReflectException.java
+++ b/src/main/javassist/tools/reflect/CannotReflectException.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,6 +17,7 @@
package javassist.tools.reflect;
import javassist.CannotCompileException;
+import javassist.CtClass;
/**
* Thrown by <code>makeReflective()</code> in <code>Reflection</code>
@@ -28,6 +30,9 @@ import javassist.CannotCompileException;
* @see javassist.CannotCompileException
*/
public class CannotReflectException extends CannotCompileException {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public CannotReflectException(String msg) {
super(msg);
}
diff --git a/src/main/javassist/tools/reflect/ClassMetaobject.java b/src/main/javassist/tools/reflect/ClassMetaobject.java
index 208d104..a00622c 100644
--- a/src/main/javassist/tools/reflect/ClassMetaobject.java
+++ b/src/main/javassist/tools/reflect/ClassMetaobject.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,12 +16,14 @@
package javassist.tools.reflect;
-import java.lang.reflect.*;
-import java.util.Arrays;
-import java.io.Serializable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
/**
* A runtime class metaobject.
@@ -32,13 +35,15 @@ import java.io.ObjectOutputStream;
* <p>To obtain a class metaobject, calls <code>_getClass()</code>
* on a reflective object. For example,
*
- * <ul><pre>ClassMetaobject cm = ((Metalevel)reflectiveObject)._getClass();
- * </pre></ul>
+ * <pre>ClassMetaobject cm = ((Metalevel)reflectiveObject)._getClass();
+ * </pre>
*
* @see javassist.tools.reflect.Metaobject
* @see javassist.tools.reflect.Metalevel
*/
public class ClassMetaobject implements Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
/**
* The base-level methods controlled by a metaobject
* are renamed so that they begin with
@@ -47,15 +52,15 @@ public class ClassMetaobject implements Serializable {
static final String methodPrefix = "_m_";
static final int methodPrefixLen = 3;
- private Class javaClass;
- private Constructor[] constructors;
+ private Class<?> javaClass;
+ private Constructor<?>[] constructors;
private Method[] methods;
/**
* Specifies how a <code>java.lang.Class</code> object is loaded.
*
* <p>If true, it is loaded by:
- * <ul><pre>Thread.currentThread().getContextClassLoader().loadClass()</pre></ul>
+ * <pre>Thread.currentThread().getContextClassLoader().loadClass()</pre>
* <p>If false, it is loaded by <code>Class.forName()</code>.
* The default value is false.
*/
@@ -94,18 +99,17 @@ public class ClassMetaobject implements Serializable {
methods = null;
}
- private Class getClassObject(String name) throws ClassNotFoundException {
+ private Class<?> getClassObject(String name) throws ClassNotFoundException {
if (useContextClassLoader)
return Thread.currentThread().getContextClassLoader()
.loadClass(name);
- else
- return Class.forName(name);
+ return Class.forName(name);
}
/**
* Obtains the <code>java.lang.Class</code> representing this class.
*/
- public final Class getJavaClass() {
+ public final Class<?> getJavaClass() {
return javaClass;
}
@@ -161,7 +165,7 @@ public class ClassMetaobject implements Serializable {
* <p>Every subclass of this class should redefine this method.
*/
public Object trapFieldRead(String name) {
- Class jc = getJavaClass();
+ Class<?> jc = getJavaClass();
try {
return jc.getField(name).get(null);
}
@@ -181,7 +185,7 @@ public class ClassMetaobject implements Serializable {
* <p>Every subclass of this class should redefine this method.
*/
public void trapFieldWrite(String name, Object value) {
- Class jc = getJavaClass();
+ Class<?> jc = getJavaClass();
try {
jc.getField(name).set(null, value);
}
@@ -250,7 +254,7 @@ public class ClassMetaobject implements Serializable {
if (methods != null)
return methods;
- Class baseclass = getJavaClass();
+ Class<?> baseclass = getJavaClass();
Method[] allmethods = baseclass.getDeclaredMethods();
int n = allmethods.length;
int[] index = new int[n];
@@ -319,7 +323,7 @@ public class ClassMetaobject implements Serializable {
* formal parameter types of the method specified
* by <code>identifier</code>.
*/
- public final Class[] getParameterTypes(int identifier) {
+ public final Class<?>[] getParameterTypes(int identifier) {
return getReflectiveMethods()[identifier].getParameterTypes();
}
@@ -327,7 +331,7 @@ public class ClassMetaobject implements Serializable {
* Returns a <code>Class</code> objects representing the
* return type of the method specified by <code>identifier</code>.
*/
- public final Class getReturnType(int identifier) {
+ public final Class<?> getReturnType(int identifier) {
return getReflectiveMethods()[identifier].getReturnType();
}
@@ -336,7 +340,7 @@ public class ClassMetaobject implements Serializable {
* original name.
*
* <p>This method is useful, in conjuction with
- * <link>ClassMetaobject#getMethod()</link>, to obtain a quick reference
+ * {@link ClassMetaobject#getMethod(int)}, to obtain a quick reference
* to the original method in the reflected class (i.e. not the proxy
* method), using the original name of the method.
*
@@ -349,7 +353,7 @@ public class ClassMetaobject implements Serializable {
*
* @see ClassMetaobject#getMethod(int)
*/
- public final int getMethodIndex(String originalName, Class[] argTypes)
+ public final int getMethodIndex(String originalName, Class<?>[] argTypes)
throws NoSuchMethodException
{
Method[] mthds = getReflectiveMethods();
diff --git a/src/main/javassist/tools/reflect/Compiler.java b/src/main/javassist/tools/reflect/Compiler.java
index 5375584..b919684 100644
--- a/src/main/javassist/tools/reflect/Compiler.java
+++ b/src/main/javassist/tools/reflect/Compiler.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,10 +16,11 @@
package javassist.tools.reflect;
-import javassist.CtClass;
-import javassist.ClassPool;
import java.io.PrintStream;
+import javassist.ClassPool;
+import javassist.CtClass;
+
class CompiledClass {
public String classname;
public String metaobject;
@@ -49,8 +51,8 @@ class CompiledClass {
* by that class name is not reflective.
*
* <p>For example,
- * <ul><pre>% java Compiler Dog -m MetaDog -c CMetaDog Cat -m MetaCat Cow
- * </pre></ul>
+ * <pre>% java Compiler Dog -m MetaDog -c CMetaDog Cat -m MetaCat Cow
+ * </pre>
*
* <p>modifies class files <code>Dog.class</code>, <code>Cat.class</code>,
* and <code>Cow.class</code>.
diff --git a/src/main/javassist/tools/reflect/Loader.java b/src/main/javassist/tools/reflect/Loader.java
index a9aef4b..f144877 100644
--- a/src/main/javassist/tools/reflect/Loader.java
+++ b/src/main/javassist/tools/reflect/Loader.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,8 +17,8 @@
package javassist.tools.reflect;
import javassist.CannotCompileException;
-import javassist.NotFoundException;
import javassist.ClassPool;
+import javassist.NotFoundException;
/**
* A class loader for reflection.
@@ -26,7 +27,7 @@ import javassist.ClassPool;
* including a reflective class,
* you must write a start-up program as follows:
*
- * <ul><pre>
+ * <pre>
* public class Main {
* public static void main(String[] args) throws Throwable {
* javassist.tools.reflect.Loader cl
@@ -36,11 +37,11 @@ import javassist.ClassPool;
* cl.run("MyApp", args);
* }
* }
- * </pre></ul>
+ * </pre>
*
* <p>Then run this program as follows:
*
- * <ul><pre>% java javassist.tools.reflect.Loader Main arg1, ...</pre></ul>
+ * <pre>% java javassist.tools.reflect.Loader Main arg1, ...</pre>
*
* <p>This command runs <code>Main.main()</code> with <code>arg1</code>, ...
* and <code>Main.main()</code> runs <code>MyApp.main()</code> with
@@ -51,7 +52,7 @@ import javassist.ClassPool;
*
* <p>Also, you can run <code>MyApp</code> in a slightly different way:
*
- * <ul><pre>
+ * <pre>
* public class Main2 {
* public static void main(String[] args) throws Throwable {
* javassist.tools.reflect.Loader cl = new javassist.tools.reflect.Loader();
@@ -60,11 +61,11 @@ import javassist.ClassPool;
* cl.run("MyApp", args);
* }
* }
- * </pre></ul>
+ * </pre>
*
* <p>This program is run as follows:
*
- * <ul><pre>% java Main2 arg1, ...</pre></ul>
+ * <pre>% java Main2 arg1, ...</pre>
*
* <p>The difference from the former one is that the class <code>Main</code>
* is loaded by <code>javassist.tools.reflect.Loader</code> whereas the class
@@ -77,7 +78,7 @@ import javassist.ClassPool;
*
* <p>The class <code>Main2</code> is equivalent to this class:
*
- * <ul><pre>
+ * <pre>
* public class Main3 {
* public static void main(String[] args) throws Throwable {
* Reflection reflection = new Reflection();
@@ -88,7 +89,7 @@ import javassist.ClassPool;
* cl.run("MyApp", args);
* }
* }
- * </pre></ul>
+ * </pre>
*
* <p><b>Note:</b>
*
@@ -114,11 +115,9 @@ public class Loader extends javassist.Loader {
* and calls <code>main()</code> in that class.
*
* @param args command line parameters.
- * <ul>
- * <code>args[0]</code> is the class name to be loaded.
- * <br><code>args[1..n]</code> are parameters passed
+ * <br>&nbsp;&nbsp;<code>args[0]</code> is the class name to be loaded.
+ * <br>&nbsp;&nbsp;<code>args[1..n]</code> are parameters passed
* to the target <code>main()</code>.
- * </ul>
*/
public static void main(String[] args) throws Throwable {
Loader cl = new Loader();
diff --git a/src/main/javassist/tools/reflect/Metalevel.java b/src/main/javassist/tools/reflect/Metalevel.java
index 4361fac..83650fd 100644
--- a/src/main/javassist/tools/reflect/Metalevel.java
+++ b/src/main/javassist/tools/reflect/Metalevel.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/tools/reflect/Metaobject.java b/src/main/javassist/tools/reflect/Metaobject.java
index cd3a5f5..d3adcb1 100644
--- a/src/main/javassist/tools/reflect/Metaobject.java
+++ b/src/main/javassist/tools/reflect/Metaobject.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,11 +16,11 @@
package javassist.tools.reflect;
-import java.lang.reflect.Method;
-import java.io.Serializable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Method;
/**
* A runtime metaobject.
@@ -36,13 +37,16 @@ import java.io.ObjectOutputStream;
* <p>To obtain a metaobject, calls <code>_getMetaobject()</code>
* on a reflective object. For example,
*
- * <ul><pre>Metaobject m = ((Metalevel)reflectiveObject)._getMetaobject();
- * </pre></ul>
+ * <pre>
+ * Metaobject m = ((Metalevel)reflectiveObject)._getMetaobject();
+ * </pre>
*
* @see javassist.tools.reflect.ClassMetaobject
* @see javassist.tools.reflect.Metalevel
*/
public class Metaobject implements Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
protected ClassMetaobject classmetaobject;
protected Metalevel baseobject;
protected Method[] methods;
@@ -136,7 +140,7 @@ public class Metaobject implements Serializable {
* formal parameter types of the method specified
* by <code>identifier</code>.
*/
- public final Class[] getParameterTypes(int identifier) {
+ public final Class<?>[] getParameterTypes(int identifier) {
return methods[identifier].getParameterTypes();
}
@@ -144,7 +148,7 @@ public class Metaobject implements Serializable {
* Returns a <code>Class</code> objects representing the
* return type of the method specified by <code>identifier</code>.
*/
- public final Class getReturnType(int identifier) {
+ public final Class<?> getReturnType(int identifier) {
return methods[identifier].getReturnType();
}
@@ -156,7 +160,7 @@ public class Metaobject implements Serializable {
* <p>Every subclass of this class should redefine this method.
*/
public Object trapFieldRead(String name) {
- Class jc = getClassMetaobject().getJavaClass();
+ Class<?> jc = getClassMetaobject().getJavaClass();
try {
return jc.getField(name).get(getObject());
}
@@ -176,7 +180,7 @@ public class Metaobject implements Serializable {
* <p>Every subclass of this class should redefine this method.
*/
public void trapFieldWrite(String name, Object value) {
- Class jc = getClassMetaobject().getJavaClass();
+ Class<?> jc = getClassMetaobject().getJavaClass();
try {
jc.getField(name).set(getObject(), value);
}
@@ -198,7 +202,8 @@ public class Metaobject implements Serializable {
* <p>Note: this method is not invoked if the base-level method
* is invoked by a constructor in the super class. For example,
*
- * <ul><pre>abstract class A {
+ * <pre>
+ * abstract class A {
* abstract void initialize();
* A() {
* initialize(); // not intercepted
@@ -211,7 +216,7 @@ public class Metaobject implements Serializable {
* super();
* initialize(); // intercepted
* }
- * }</pre></ul>
+ * }</pre>
*
* <p>if an instance of B is created,
* the invocation of initialize() in B is intercepted only once.
diff --git a/src/main/javassist/tools/reflect/Reflection.java b/src/main/javassist/tools/reflect/Reflection.java
index cb93096..dddd9a4 100644
--- a/src/main/javassist/tools/reflect/Reflection.java
+++ b/src/main/javassist/tools/reflect/Reflection.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,8 +16,20 @@
package javassist.tools.reflect;
-import javassist.*;
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CodeConverter;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
import javassist.CtMethod.ConstParameter;
+import javassist.CtNewMethod;
+import javassist.Modifier;
+import javassist.NotFoundException;
+import javassist.Translator;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.MethodInfo;
/**
* The class implementing the behavioral reflection mechanism.
@@ -30,16 +43,16 @@ import javassist.CtMethod.ConstParameter;
*
* <p>To do this, the original class file representing a reflective class:
*
- * <ul><pre>
+ * <pre>
* class Person {
* public int f(int i) { return i + 1; }
* public int value;
* }
- * </pre></ul>
+ * </pre>
*
* <p>is modified so that it represents a class:
*
- * <ul><pre>
+ * <pre>
* class Person implements Metalevel {
* public int _original_f(int i) { return i + 1; }
* public int f(int i) { <i>delegate to the metaobject</i> }
@@ -52,7 +65,7 @@ import javassist.CtMethod.ConstParameter;
* public Metaobject _getMetaobject() { <i>return a metaobject</i> }
* public void _setMetaobject(Metaobject m) { <i>change a metaobject</i> }
* }
- * </pre></ul>
+ * </pre>
*
* @see javassist.tools.reflect.ClassMetaobject
* @see javassist.tools.reflect.Metaobject
@@ -100,12 +113,14 @@ public class Reflection implements Translator {
/**
* Initializes the object.
*/
+ @Override
public void start(ClassPool pool) throws NotFoundException {
classPool = pool;
final String msg
= "javassist.tools.reflect.Sample is not found or broken.";
try {
CtClass c = classPool.get("javassist.tools.reflect.Sample");
+ rebuildClassFile(c.getClassFile());
trapMethod = c.getDeclaredMethod("trap");
trapStaticMethod = c.getDeclaredMethod("trapStatic");
trapRead = c.getDeclaredMethod("trapRead");
@@ -115,6 +130,8 @@ public class Reflection implements Translator {
}
catch (NotFoundException e) {
throw new RuntimeException(msg);
+ } catch (BadBytecode e) {
+ throw new RuntimeException(msg);
}
}
@@ -122,6 +139,7 @@ public class Reflection implements Translator {
* Inserts hooks for intercepting accesses to the fields declared
* in reflective classes.
*/
+ @Override
public void onLoad(ClassPool pool, String classname)
throws CannotCompileException, NotFoundException
{
@@ -168,8 +186,8 @@ public class Reflection implements Translator {
* @see javassist.tools.reflect.Metaobject
* @see javassist.tools.reflect.ClassMetaobject
*/
- public boolean makeReflective(Class clazz,
- Class metaobject, Class metaclass)
+ public boolean makeReflective(Class<?> clazz,
+ Class<?> metaobject, Class<?> metaclass)
throws CannotCompileException, NotFoundException
{
return makeReflective(clazz.getName(), metaobject.getName(),
@@ -240,8 +258,7 @@ public class Reflection implements Translator {
{
if (clazz.getAttribute("Reflective") != null)
return false; // this is already reflective.
- else
- clazz.setAttribute("Reflective", new byte[0]);
+ clazz.setAttribute("Reflective", new byte[0]);
CtClass mlevel = classPool.get("javassist.tools.reflect.Metalevel");
boolean addMeta = !clazz.subtypeOf(mlevel);
@@ -381,4 +398,12 @@ public class Reflection implements Translator {
}
}
}
+
+ public void rebuildClassFile(ClassFile cf) throws BadBytecode {
+ if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_6)
+ return;
+
+ for (MethodInfo mi:cf.getMethods())
+ mi.rebuildStackMap(classPool);
+ }
}
diff --git a/src/main/javassist/tools/reflect/Sample.java b/src/main/javassist/tools/reflect/Sample.java
index d76df19..8a7360d 100644
--- a/src/main/javassist/tools/reflect/Sample.java
+++ b/src/main/javassist/tools/reflect/Sample.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -27,8 +28,7 @@ public class Sample {
mobj = _metaobject;
if (mobj == null)
return ClassMetaobject.invoke(this, identifier, args);
- else
- return mobj.trapMethodcall(identifier, args);
+ return mobj.trapMethodcall(identifier, args);
}
public static Object trapStatic(Object[] args, int identifier)
@@ -40,8 +40,7 @@ public class Sample {
public static Object trapRead(Object[] args, String name) {
if (args[0] == null)
return _classobject.trapFieldRead(name);
- else
- return ((Metalevel)args[0])._getMetaobject().trapFieldRead(name);
+ return ((Metalevel)args[0])._getMetaobject().trapFieldRead(name);
}
public static Object trapWrite(Object[] args, String name) {
diff --git a/src/main/javassist/tools/rmi/AppletServer.java b/src/main/javassist/tools/rmi/AppletServer.java
index b2678f3..e49ed0d 100644
--- a/src/main/javassist/tools/rmi/AppletServer.java
+++ b/src/main/javassist/tools/rmi/AppletServer.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,16 +16,26 @@
package javassist.tools.rmi;
-import java.io.*;
-
-import javassist.tools.web.*;
-import javassist.CannotCompileException;
-import javassist.NotFoundException;
-import javassist.ClassPool;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidClassException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
import java.util.Vector;
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.NotFoundException;
+import javassist.tools.web.BadHttpRequest;
+import javassist.tools.web.Webserver;
+
/**
* An AppletServer object is a web server that an ObjectImporter
* communicates with. It makes the objects specified by
@@ -36,8 +47,8 @@ import java.util.Vector;
*/
public class AppletServer extends Webserver {
private StubGenerator stubGen;
- private Hashtable exportedNames;
- private Vector exportedObjects;
+ private Map<String,ExportedObject> exportedNames;
+ private List<ExportedObject> exportedObjects;
private static final byte[] okHeader
= "HTTP/1.0 200 OK\r\n\r\n".getBytes();
@@ -80,8 +91,8 @@ public class AppletServer extends Webserver {
throws IOException, NotFoundException, CannotCompileException
{
super(port);
- exportedNames = new Hashtable();
- exportedObjects = new Vector();
+ exportedNames = new Hashtable<String,ExportedObject>();
+ exportedObjects = new Vector<ExportedObject>();
stubGen = gen;
addTranslator(loader, gen);
}
@@ -89,6 +100,7 @@ public class AppletServer extends Webserver {
/**
* Begins the HTTP service.
*/
+ @Override
public void run() {
super.run();
}
@@ -108,11 +120,11 @@ public class AppletServer extends Webserver {
public synchronized int exportObject(String name, Object obj)
throws CannotCompileException
{
- Class clazz = obj.getClass();
+ Class<?> clazz = obj.getClass();
ExportedObject eo = new ExportedObject();
eo.object = obj;
eo.methods = clazz.getMethods();
- exportedObjects.addElement(eo);
+ exportedObjects.add(eo);
eo.identifier = exportedObjects.size() - 1;
if (name != null)
exportedNames.put(name, eo);
@@ -130,6 +142,7 @@ public class AppletServer extends Webserver {
/**
* Processes a request from a web browser (an ObjectImporter).
*/
+ @Override
public void doReply(InputStream in, OutputStream out, String cmd)
throws IOException, BadHttpRequest
{
@@ -151,8 +164,7 @@ public class AppletServer extends Webserver {
Exception err = null;
Object rvalue = null;
try {
- ExportedObject eo
- = (ExportedObject)exportedObjects.elementAt(objectId);
+ ExportedObject eo = exportedObjects.get(objectId);
Object[] args = readParameters(in);
rvalue = convertRvalue(eo.methods[methodId].invoke(eo.object,
args));
@@ -194,8 +206,7 @@ public class AppletServer extends Webserver {
Object a = in.readObject();
if (a instanceof RemoteRef) {
RemoteRef ref = (RemoteRef)a;
- ExportedObject eo
- = (ExportedObject)exportedObjects.elementAt(ref.oid);
+ ExportedObject eo = exportedObjects.get(ref.oid);
a = eo.object;
}
@@ -214,8 +225,7 @@ public class AppletServer extends Webserver {
String classname = rvalue.getClass().getName();
if (stubGen.isProxyClass(classname))
return new RemoteRef(exportObject(null, rvalue), classname);
- else
- return rvalue;
+ return rvalue;
}
private void lookupName(String cmd, InputStream ins, OutputStream outs)
@@ -223,7 +233,7 @@ public class AppletServer extends Webserver {
{
ObjectInputStream in = new ObjectInputStream(ins);
String name = DataInputStream.readUTF(in);
- ExportedObject found = (ExportedObject)exportedNames.get(name);
+ ExportedObject found = exportedNames.get(name);
outs.write(okHeader);
ObjectOutputStream out = new ObjectOutputStream(outs);
if (found == null) {
diff --git a/src/main/javassist/tools/rmi/ObjectImporter.java b/src/main/javassist/tools/rmi/ObjectImporter.java
index e6692ef..798279a 100644
--- a/src/main/javassist/tools/rmi/ObjectImporter.java
+++ b/src/main/javassist/tools/rmi/ObjectImporter.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,10 +16,16 @@
package javassist.tools.rmi;
-import java.io.*;
-import java.net.*;
-import java.applet.Applet;
-import java.lang.reflect.*;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.net.Socket;
+import java.net.URL;
/**
* The object importer enables applets to call a method on a remote
@@ -72,6 +79,8 @@ import java.lang.reflect.*;
* @see javassist.tools.web.Viewer
*/
public class ObjectImporter implements java.io.Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private final byte[] endofline = { 0x0d, 0x0a };
private String servername, orgServername;
private int port, orgPort;
@@ -87,7 +96,8 @@ public class ObjectImporter implements java.io.Serializable {
*
* @param applet the applet loaded from the <code>Webserver</code>.
*/
- public ObjectImporter(Applet applet) {
+ public ObjectImporter(@SuppressWarnings("deprecation") java.applet.Applet applet) {
+ @SuppressWarnings("deprecation")
URL codebase = applet.getCodeBase();
orgServername = servername = codebase.getHost();
orgPort = port = codebase.getPort();
@@ -99,10 +109,10 @@ public class ObjectImporter implements java.io.Serializable {
* <p>If you run a program with <code>javassist.tools.web.Viewer</code>,
* you can construct an object importer as follows:
*
- * <ul><pre>
+ * <pre>
* Viewer v = (Viewer)this.getClass().getClassLoader();
* ObjectImporter oi = new ObjectImporter(v.getServer(), v.getPort());
- * </pre></ul>
+ * </pre>
*
* @see javassist.tools.web.Viewer
*/
@@ -182,13 +192,13 @@ public class ObjectImporter implements java.io.Serializable {
throw new ObjectNotFoundException(name);
}
- private static final Class[] proxyConstructorParamTypes
+ private static final Class<?>[] proxyConstructorParamTypes
= new Class[] { ObjectImporter.class, int.class };
private Object createProxy(int oid, String classname) throws Exception {
- Class c = Class.forName(classname);
- Constructor cons = c.getConstructor(proxyConstructorParamTypes);
- return cons.newInstance(new Object[] { this, new Integer(oid) });
+ Class<?> c = Class.forName(classname);
+ Constructor<?> cons = c.getConstructor(proxyConstructorParamTypes);
+ return cons.newInstance(new Object[] { this, Integer.valueOf(oid) });
}
/**
@@ -266,8 +276,7 @@ public class ObjectImporter implements java.io.Serializable {
if (result)
return rvalue;
- else
- throw new RemoteException(errmsg);
+ throw new RemoteException(errmsg);
}
private void skipHeader(InputStream in) throws IOException {
diff --git a/src/main/javassist/tools/rmi/ObjectNotFoundException.java b/src/main/javassist/tools/rmi/ObjectNotFoundException.java
index 8ec3a46..bd5b27e 100644
--- a/src/main/javassist/tools/rmi/ObjectNotFoundException.java
+++ b/src/main/javassist/tools/rmi/ObjectNotFoundException.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -16,6 +17,9 @@
package javassist.tools.rmi;
public class ObjectNotFoundException extends Exception {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public ObjectNotFoundException(String name) {
super(name + " is not exported");
}
diff --git a/src/main/javassist/tools/rmi/Proxy.java b/src/main/javassist/tools/rmi/Proxy.java
index 5ea8a70..43fa721 100644
--- a/src/main/javassist/tools/rmi/Proxy.java
+++ b/src/main/javassist/tools/rmi/Proxy.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/tools/rmi/RemoteException.java b/src/main/javassist/tools/rmi/RemoteException.java
index 19a107f..6d4612a 100644
--- a/src/main/javassist/tools/rmi/RemoteException.java
+++ b/src/main/javassist/tools/rmi/RemoteException.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -20,6 +21,9 @@ package javassist.tools.rmi;
* during remote method invocation.
*/
public class RemoteException extends RuntimeException {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public RemoteException(String msg) {
super(msg);
}
diff --git a/src/main/javassist/tools/rmi/RemoteRef.java b/src/main/javassist/tools/rmi/RemoteRef.java
index fb1c2e1..4537352 100644
--- a/src/main/javassist/tools/rmi/RemoteRef.java
+++ b/src/main/javassist/tools/rmi/RemoteRef.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -20,6 +21,8 @@ package javassist.tools.rmi;
* reference through a network stream.
*/
public class RemoteRef implements java.io.Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
public int oid;
public String classname;
diff --git a/src/main/javassist/tools/rmi/Sample.java b/src/main/javassist/tools/rmi/Sample.java
index 12c799b..1be86da 100644
--- a/src/main/javassist/tools/rmi/Sample.java
+++ b/src/main/javassist/tools/rmi/Sample.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/tools/rmi/StubGenerator.java b/src/main/javassist/tools/rmi/StubGenerator.java
index 8b6604a..b0817ab 100644
--- a/src/main/javassist/tools/rmi/StubGenerator.java
+++ b/src/main/javassist/tools/rmi/StubGenerator.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,17 +16,29 @@
package javassist.tools.rmi;
-import javassist.*;
import java.lang.reflect.Method;
import java.util.Hashtable;
+import java.util.Map;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtField;
+import javassist.CtMethod;
import javassist.CtMethod.ConstParameter;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import javassist.Modifier;
+import javassist.NotFoundException;
+import javassist.Translator;
/**
* A stub-code generator. It is used for producing a proxy class.
*
* <p>The proxy class for class A is as follows:
*
- * <ul><pre>public class A implements Proxy, Serializable {
+ * <pre>public class A implements Proxy, Serializable {
* private ObjectImporter importer;
* private int objectId;
* public int _getObjectId() { return objectId; }
@@ -34,7 +47,7 @@ import javassist.CtMethod.ConstParameter;
* }
*
* ... the same methods that the original class A declares ...
- * }</pre></ul>
+ * }</pre>
*
* <p>Instances of the proxy class is created by an
* <code>ObjectImporter</code> object.
@@ -46,7 +59,7 @@ public class StubGenerator implements Translator {
private static final String sampleClass = "javassist.tools.rmi.Sample";
private ClassPool classPool;
- private Hashtable proxyClasses;
+ private Map<String,CtClass> proxyClasses;
private CtMethod forwardMethod;
private CtMethod forwardStaticMethod;
@@ -58,7 +71,7 @@ public class StubGenerator implements Translator {
* Constructs a stub-code generator.
*/
public StubGenerator() {
- proxyClasses = new Hashtable();
+ proxyClasses = new Hashtable<String,CtClass>();
}
/**
@@ -67,6 +80,7 @@ public class StubGenerator implements Translator {
*
* @see javassist.Translator#start(ClassPool)
*/
+ @Override
public void start(ClassPool pool) throws NotFoundException {
classPool = pool;
CtClass c = pool.get(sampleClass);
@@ -88,6 +102,7 @@ public class StubGenerator implements Translator {
* This is a method declared in javassist.Translator.
* @see javassist.Translator#onLoad(ClassPool,String)
*/
+ @Override
public void onLoad(ClassPool pool, String classname) {}
/**
@@ -109,22 +124,20 @@ public class StubGenerator implements Translator {
* @return <code>false</code> if the proxy class
* has been already produced.
*/
- public synchronized boolean makeProxyClass(Class clazz)
+ public synchronized boolean makeProxyClass(Class<?> clazz)
throws CannotCompileException, NotFoundException
{
String classname = clazz.getName();
if (proxyClasses.get(classname) != null)
return false;
- else {
- CtClass ctclazz = produceProxyClass(classPool.get(classname),
- clazz);
- proxyClasses.put(classname, ctclazz);
- modifySuperclass(ctclazz);
- return true;
- }
+ CtClass ctclazz = produceProxyClass(classPool.get(classname),
+ clazz);
+ proxyClasses.put(classname, ctclazz);
+ modifySuperclass(ctclazz);
+ return true;
}
- private CtClass produceProxyClass(CtClass orgclass, Class orgRtClass)
+ private CtClass produceProxyClass(CtClass orgclass, Class<?> orgRtClass)
throws CannotCompileException, NotFoundException
{
int modify = orgclass.getModifiers();
@@ -165,7 +178,7 @@ public class StubGenerator implements Translator {
}
}
- private CtClass toCtClass(Class rtclass) throws NotFoundException {
+ private CtClass toCtClass(Class<?> rtclass) throws NotFoundException {
String name;
if (!rtclass.isArray())
name = rtclass.getName();
@@ -178,11 +191,11 @@ public class StubGenerator implements Translator {
sbuf.insert(0, rtclass.getName());
name = sbuf.toString();
}
-
+
return classPool.get(name);
}
- private CtClass[] toCtClass(Class[] rtclasses) throws NotFoundException {
+ private CtClass[] toCtClass(Class<?>[] rtclasses) throws NotFoundException {
int n = rtclasses.length;
CtClass[] ctclasses = new CtClass[n];
for (int i = 0; i < n; ++i)
diff --git a/src/main/javassist/tools/web/BadHttpRequest.java b/src/main/javassist/tools/web/BadHttpRequest.java
index 0010203..52ef4f5 100644
--- a/src/main/javassist/tools/web/BadHttpRequest.java
+++ b/src/main/javassist/tools/web/BadHttpRequest.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -19,16 +20,18 @@ package javassist.tools.web;
* Thrown when receiving an invalid HTTP request.
*/
public class BadHttpRequest extends Exception {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private Exception e;
public BadHttpRequest() { e = null; }
public BadHttpRequest(Exception _e) { e = _e; }
+ @Override
public String toString() {
if (e == null)
return super.toString();
- else
- return e.toString();
+ return e.toString();
}
}
diff --git a/src/main/javassist/tools/web/Viewer.java b/src/main/javassist/tools/web/Viewer.java
index de7afae..8fb015f 100644
--- a/src/main/javassist/tools/web/Viewer.java
+++ b/src/main/javassist/tools/web/Viewer.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,8 +16,10 @@
package javassist.tools.web;
-import java.io.*;
-import java.net.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
/**
* A sample applet viewer.
@@ -27,7 +30,7 @@ import java.net.*;
*
* <p>To run, you should type:
*
- * <ul><code>% java javassist.tools.web.Viewer <i>host port</i> Main arg1, ...</code></ul>
+ * <pre>% java javassist.tools.web.Viewer <i>host port</i> Main arg1, ...</pre>
*
* <p>This command calls <code>Main.main()</code> with <code>arg1,...</code>
* All classes including <code>Main</code> are fetched from
@@ -41,10 +44,10 @@ import java.net.*;
* a program loaded by this object can call a method in <code>Viewer</code>.
* For example, you can write something like this:
*
- * <ul><pre>
+ * <pre>
* Viewer v = (Viewer)this.getClass().getClassLoader();
* String port = v.getPort();
- * </pre></ul>
+ * </pre>
*
*/
public class Viewer extends ClassLoader {
@@ -96,7 +99,7 @@ public class Viewer extends ClassLoader {
public void run(String classname, String[] args)
throws Throwable
{
- Class c = loadClass(classname);
+ Class<?> c = loadClass(classname);
try {
c.getDeclaredMethod("main", new Class[] { String[].class })
.invoke(null, new Object[] { args });
@@ -109,10 +112,11 @@ public class Viewer extends ClassLoader {
/**
* Requests the class loader to load a class.
*/
- protected synchronized Class loadClass(String name, boolean resolve)
+ @Override
+ protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
- Class c = findLoadedClass(name);
+ Class<?> c = findLoadedClass(name);
if (c == null)
c = findClass(name);
@@ -135,8 +139,9 @@ public class Viewer extends ClassLoader {
* <p>This method can be overridden by a subclass of
* <code>Viewer</code>.
*/
- protected Class findClass(String name) throws ClassNotFoundException {
- Class c = null;
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ Class<?> c = null;
if (name.startsWith("java.") || name.startsWith("javax.")
|| name.equals("javassist.tools.web.Viewer"))
c = findSystemClass(name);
diff --git a/src/main/javassist/tools/web/Webserver.java b/src/main/javassist/tools/web/Webserver.java
index 952d56d..d90e8e3 100644
--- a/src/main/javassist/tools/web/Webserver.java
+++ b/src/main/javassist/tools/web/Webserver.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,10 +16,23 @@
package javassist.tools.web;
-import java.net.*;
-import java.io.*;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
import java.util.Date;
-import javassist.*;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.NotFoundException;
+import javassist.Translator;
/**
* A web server for running sample programs.
@@ -277,8 +291,7 @@ public class Webserver {
len = fin.read(filebuffer);
if (len <= 0)
break;
- else
- out.write(filebuffer, 0, len);
+ out.write(filebuffer, 0, len);
}
fin.close();
@@ -298,8 +311,7 @@ public class Webserver {
len = fin.read(filebuffer);
if (len <= 0)
break;
- else
- barray.write(filebuffer, 0, len);
+ barray.write(filebuffer, 0, len);
}
byte[] classfile = barray.toByteArray();
@@ -397,6 +409,7 @@ class ServiceThread extends Thread {
sock = s;
}
+ @Override
public void run() {
try {
web.process(sock);
diff --git a/src/main/javassist/util/HotSwapAgent.java b/src/main/javassist/util/HotSwapAgent.java
new file mode 100644
index 0000000..e696ce2
--- /dev/null
+++ b/src/main/javassist/util/HotSwapAgent.java
@@ -0,0 +1,223 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+package javassist.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.instrument.ClassDefinition;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.lang.management.ManagementFactory;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import com.sun.tools.attach.VirtualMachine;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.NotFoundException;
+
+/**
+ * A utility class for dynamically adding a new method
+ * or modifying an existing method body.
+ * This class provides {@link #redefine(Class, CtClass)}
+ * and {@link #redefine(Class[], CtClass[])}, which replace the
+ * existing class definition with a new one.
+ * These methods perform the replacement by
+ * {@code java.lang.instrument.Instrumentation}. For details
+ * of acceptable modification,
+ * see the {@code Instrumentation} interface.
+ *
+ * <p>Before calling the {@code redefine} methods, the hotswap agent
+ * has to be deployed.</p>
+ *
+ * <p>To create a hotswap agent, run {@link #createAgentJarFile(String)}.
+ * For example, the following command creates an agent file named {@code hotswap.jar}.
+ *
+ * <pre>
+ * $ jshell --class-path javassist.jar
+ * jshell&gt; javassist.util.HotSwapAgent.createAgentJarFile("hotswap.jar")
+ * </pre>
+ *
+ * <p>Then, run the JVM with the VM argument {@code -javaagent:hotswap.jar}
+ * to deploy the hotswap agent.
+ * </p>
+ *
+ * <p>If the {@code -javaagent} option is not given to the JVM, {@code HotSwapAgent}
+ * attempts to automatically create and start the hotswap agent on demand.
+ * This automated deployment may fail. If it fails, manually create the hotswap agent
+ * and deploy it by {@code -javaagent}.</p>
+ *
+ * <p>The {@code HotSwapAgent} requires {@code tools.jar} as well as {@code javassist.jar}.</p>
+ *
+ * <p>The idea of this class was given by <a href="https://github.com/alugowski">Adam Lugowski</a>.
+ * Shigeru Chiba wrote this class by referring
+ * to his <a href="https://github.com/turn/RedefineClassAgent">{@code RedefineClassAgent}</a>.
+ * For details, see <a href="https://github.com/jboss-javassist/javassist/issues/119">this discussion</a>.
+ * </p>
+ *
+ * @see #redefine(Class, CtClass)
+ * @see #redefine(Class[], CtClass[])
+ * @since 3.22
+ */
+public class HotSwapAgent {
+ private static Instrumentation instrumentation = null;
+
+ /**
+ * Obtains the {@code Instrumentation} object.
+ *
+ * @return null when it is not available.
+ */
+ public Instrumentation instrumentation() { return instrumentation; }
+
+ /**
+ * The entry point invoked when this agent is started by {@code -javaagent}.
+ */
+ public static void premain(String agentArgs, Instrumentation inst) throws Throwable {
+ agentmain(agentArgs, inst);
+ }
+
+ /**
+ * The entry point invoked when this agent is started after the JVM starts.
+ */
+ public static void agentmain(String agentArgs, Instrumentation inst) throws Throwable {
+ if (!inst.isRedefineClassesSupported())
+ throw new RuntimeException("this JVM does not support redefinition of classes");
+
+ instrumentation = inst;
+ }
+
+ /**
+ * Redefines a class.
+ */
+ public static void redefine(Class<?> oldClass, CtClass newClass)
+ throws NotFoundException, IOException, CannotCompileException
+ {
+ Class<?>[] old = { oldClass };
+ CtClass[] newClasses = { newClass };
+ redefine(old, newClasses);
+ }
+
+ /**
+ * Redefines classes.
+ */
+ public static void redefine(Class<?>[] oldClasses, CtClass[] newClasses)
+ throws NotFoundException, IOException, CannotCompileException
+ {
+ startAgent();
+ ClassDefinition[] defs = new ClassDefinition[oldClasses.length];
+ for (int i = 0; i < oldClasses.length; i++)
+ defs[i] = new ClassDefinition(oldClasses[i], newClasses[i].toBytecode());
+
+ try {
+ instrumentation.redefineClasses(defs);
+ }
+ catch (ClassNotFoundException e) {
+ throw new NotFoundException(e.getMessage(), e);
+ }
+ catch (UnmodifiableClassException e) {
+ throw new CannotCompileException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Ensures that the agent is ready.
+ * It attempts to dynamically start the agent if necessary.
+ */
+ private static void startAgent() throws NotFoundException {
+ if (instrumentation != null)
+ return;
+
+ try {
+ File agentJar = createJarFile();
+
+ String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
+ String pid = nameOfRunningVM.substring(0, nameOfRunningVM.indexOf('@'));
+ VirtualMachine vm = VirtualMachine.attach(pid);
+ vm.loadAgent(agentJar.getAbsolutePath(), null);
+ vm.detach();
+ }
+ catch (Exception e) {
+ throw new NotFoundException("hotswap agent", e);
+ }
+
+ for (int sec = 0; sec < 10 /* sec */; sec++) {
+ if (instrumentation != null)
+ return;
+
+ try {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+
+ throw new NotFoundException("hotswap agent (timeout)");
+ }
+
+ /**
+ * Creates an agent file for using {@code HotSwapAgent}.
+ */
+ public static File createAgentJarFile(String fileName)
+ throws IOException, CannotCompileException, NotFoundException
+ {
+ return createJarFile(new File(fileName));
+ }
+
+ private static File createJarFile()
+ throws IOException, CannotCompileException, NotFoundException
+ {
+ File jar = File.createTempFile("agent", ".jar");
+ jar.deleteOnExit();
+ return createJarFile(jar);
+ }
+
+ private static File createJarFile(File jar)
+ throws IOException, CannotCompileException, NotFoundException
+ {
+ Manifest manifest = new Manifest();
+ Attributes attrs = manifest.getMainAttributes();
+ attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
+ attrs.put(new Attributes.Name("Premain-Class"), HotSwapAgent.class.getName());
+ attrs.put(new Attributes.Name("Agent-Class"), HotSwapAgent.class.getName());
+ attrs.put(new Attributes.Name("Can-Retransform-Classes"), "true");
+ attrs.put(new Attributes.Name("Can-Redefine-Classes"), "true");
+
+ JarOutputStream jos = null;
+ try {
+ jos = new JarOutputStream(new FileOutputStream(jar), manifest);
+ String cname = HotSwapAgent.class.getName();
+ JarEntry e = new JarEntry(cname.replace('.', '/') + ".class");
+ jos.putNextEntry(e);
+ ClassPool pool = ClassPool.getDefault();
+ CtClass clazz = pool.get(cname);
+ jos.write(clazz.toBytecode());
+ jos.closeEntry();
+ }
+ finally {
+ if (jos != null)
+ jos.close();
+ }
+
+ return jar;
+ }
+}
diff --git a/src/main/javassist/util/HotSwapper.java b/src/main/javassist/util/HotSwapper.java
index 3536adf..5d64ee5 100644
--- a/src/main/javassist/util/HotSwapper.java
+++ b/src/main/javassist/util/HotSwapper.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,12 +16,25 @@
package javassist.util;
-import com.sun.jdi.*;
-import com.sun.jdi.connect.*;
-import com.sun.jdi.event.*;
-import com.sun.jdi.request.*;
-import java.io.*;
-import java.util.*;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.connect.AttachingConnector;
+import com.sun.jdi.connect.Connector;
+import com.sun.jdi.connect.IllegalConnectorArgumentsException;
+import com.sun.jdi.event.Event;
+import com.sun.jdi.event.EventIterator;
+import com.sun.jdi.event.EventQueue;
+import com.sun.jdi.event.EventSet;
+import com.sun.jdi.event.MethodEntryEvent;
+import com.sun.jdi.request.EventRequest;
+import com.sun.jdi.request.EventRequestManager;
+import com.sun.jdi.request.MethodEntryRequest;
class Trigger {
void doSwap() {}
@@ -28,7 +42,7 @@ class Trigger {
/**
* A utility class for dynamically reloading a class by
- * the Java Platform Debugger Architecture (JPDA), or <it>HotSwap</code>.
+ * the Java Platform Debugger Architecture (JPDA), or <i>HotSwap</i>.
* It works only with JDK 1.4 and later.
*
* <p><b>Note:</b> The new definition of the reloaded class must declare
@@ -39,12 +53,10 @@ class Trigger {
* <p>To use this class, the JVM must be launched with the following
* command line options:
*
- * <ul>
* <p>For Java 1.4,<br>
* <pre>java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000</pre>
* <p>For Java 5,<br>
* <pre>java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000</pre>
- * </ul>
*
* <p>Note that 8000 is the port number used by <code>HotSwapper</code>.
* Any port number can be specified. Since <code>HotSwapper</code> does not
@@ -56,12 +68,12 @@ class Trigger {
*
* <p>Using <code>HotSwapper</code> is easy. See the following example:
*
- * <ul><pre>
+ * <pre>
* CtClass clazz = ...
* byte[] classFile = clazz.toBytecode();
* HotSwapper hs = new HostSwapper(8000); // 8000 is a port number.
* hs.reload("Test", classFile);
- * </pre></ul>
+ * </pre>
*
* <p><code>reload()</code>
* first unload the <code>Test</code> class and load a new version of
@@ -71,12 +83,17 @@ class Trigger {
* repatedly call <code>reload()</code> on the same <code>HotSwapper</code>
* object so that they can reload a number of classes.
*
+ * <p>{@code HotSwap} depends on the debug agent to perform hot-swapping
+ * but it is reported that the debug agent is buggy under massively multithreaded
+ * environments. If you encounter a problem, try {@link HotSwapAgent}.
+ *
* @since 3.1
+ * @see HotSwapAgent
*/
public class HotSwapper {
private VirtualMachine jvm;
private MethodEntryRequest request;
- private Map newClassFiles;
+ private Map<ReferenceType,byte[]> newClassFiles;
private Trigger trigger;
@@ -109,23 +126,20 @@ public class HotSwapper {
AttachingConnector connector
= (AttachingConnector)findConnector("com.sun.jdi.SocketAttach");
- Map arguments = connector.defaultArguments();
- ((Connector.Argument)arguments.get("hostname")).setValue(HOST_NAME);
- ((Connector.Argument)arguments.get("port")).setValue(port);
+ Map<String,Connector.Argument> arguments = connector.defaultArguments();
+ arguments.get("hostname").setValue(HOST_NAME);
+ arguments.get("port").setValue(port);
jvm = connector.attach(arguments);
EventRequestManager manager = jvm.eventRequestManager();
request = methodEntryRequests(manager, TRIGGER_NAME);
}
private Connector findConnector(String connector) throws IOException {
- List connectors = Bootstrap.virtualMachineManager().allConnectors();
- Iterator iter = connectors.iterator();
- while (iter.hasNext()) {
- Connector con = (Connector)iter.next();
- if (con.name().equals(connector)) {
+ List<Connector> connectors = Bootstrap.virtualMachineManager().allConnectors();
+
+ for (Connector con:connectors)
+ if (con.name().equals(connector))
return con;
- }
- }
throw new IOException("Not found: " + connector);
}
@@ -141,6 +155,7 @@ public class HotSwapper {
/* Stops triggering a hotswapper when reload() is called.
*/
+ @SuppressWarnings("unused")
private void deleteEventRequest(EventRequestManager manager,
MethodEntryRequest request) {
manager.deleteEventRequest(request);
@@ -154,7 +169,7 @@ public class HotSwapper {
*/
public void reload(String className, byte[] classFile) {
ReferenceType classtype = toRefType(className);
- Map map = new HashMap();
+ Map<ReferenceType,byte[]> map = new HashMap<ReferenceType,byte[]>();
map.put(classtype, classFile);
reload2(map, className);
}
@@ -167,14 +182,11 @@ public class HotSwapper {
* is <code>String</code> and the type of the
* class files is <code>byte[]</code>.
*/
- public void reload(Map classFiles) {
- Set set = classFiles.entrySet();
- Iterator it = set.iterator();
- Map map = new HashMap();
+ public void reload(Map<String,byte[]> classFiles) {
+ Map<ReferenceType,byte[]> map = new HashMap<ReferenceType,byte[]>();
String className = null;
- while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- className = (String)e.getKey();
+ for (Map.Entry<String,byte[]> e:classFiles.entrySet()) {
+ className = e.getKey();
map.put(toRefType(className), e.getValue());
}
@@ -183,21 +195,20 @@ public class HotSwapper {
}
private ReferenceType toRefType(String className) {
- List list = jvm.classesByName(className);
+ List<ReferenceType> list = jvm.classesByName(className);
if (list == null || list.isEmpty())
throw new RuntimeException("no such class: " + className);
- else
- return (ReferenceType)list.get(0);
+ return list.get(0);
}
- private void reload2(Map map, String msg) {
+ private void reload2(Map<ReferenceType,byte[]> map, String msg) {
synchronized (trigger) {
startDaemon();
newClassFiles = map;
request.enable();
trigger.doSwap();
request.disable();
- Map ncf = newClassFiles;
+ Map<ReferenceType,byte[]> ncf = newClassFiles;
if (ncf != null) {
newClassFiles = null;
throw new RuntimeException("failed to reload: " + msg);
@@ -212,6 +223,7 @@ public class HotSwapper {
e.printStackTrace(System.err);
}
+ @Override
public void run() {
EventSet events = null;
try {
@@ -245,7 +257,7 @@ public class HotSwapper {
}
void hotswap() {
- Map map = newClassFiles;
+ Map<ReferenceType,byte[]> map = newClassFiles;
jvm.redefineClasses(map);
newClassFiles = null;
}
diff --git a/src/main/javassist/util/proxy/DefineClassHelper.java b/src/main/javassist/util/proxy/DefineClassHelper.java
new file mode 100644
index 0000000..96ade4a
--- /dev/null
+++ b/src/main/javassist/util/proxy/DefineClassHelper.java
@@ -0,0 +1,342 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+
+package javassist.util.proxy;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Method;
+import java.security.ProtectionDomain;
+import java.util.List;
+
+import javassist.CannotCompileException;
+import javassist.bytecode.ClassFile;
+
+/**
+ * Helper class for invoking {@link ClassLoader#defineClass(String,byte[],int,int)}.
+ *
+ * @since 3.22
+ */
+public class DefineClassHelper {
+
+ private static abstract class Helper {
+ abstract Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
+ ClassLoader loader, ProtectionDomain protectionDomain)
+ throws ClassFormatError, CannotCompileException;
+ }
+
+ private static class Java11 extends JavaOther {
+ Class<?> defineClass(String name, byte[] bcode, int off, int len, Class<?> neighbor,
+ ClassLoader loader, ProtectionDomain protectionDomain)
+ throws ClassFormatError, CannotCompileException
+ {
+ if (neighbor != null)
+ return toClass(neighbor, bcode);
+ else {
+ // Lookup#defineClass() is not available. So fallback to invoking defineClass on
+ // ClassLoader, which causes a warning message.
+ return super.defineClass(name, bcode, off, len, neighbor, loader, protectionDomain);
+ }
+ }
+ }
+
+ private static class Java9 extends Helper {
+ final class ReferencedUnsafe {
+ private final SecurityActions.TheUnsafe sunMiscUnsafeTheUnsafe;
+ private final MethodHandle defineClass;
+
+ ReferencedUnsafe(SecurityActions.TheUnsafe usf, MethodHandle meth) {
+ this.sunMiscUnsafeTheUnsafe = usf;
+ this.defineClass = meth;
+ }
+
+ Class<?> defineClass(String name, byte[] b, int off, int len,
+ ClassLoader loader, ProtectionDomain protectionDomain)
+ throws ClassFormatError
+ {
+ try {
+ if (getCallerClass.invoke(stack) != Java9.class)
+ throw new IllegalAccessError("Access denied for caller.");
+ } catch (Exception e) {
+ throw new RuntimeException("cannot initialize", e);
+ }
+ try {
+ return (Class<?>) defineClass.invokeWithArguments(
+ sunMiscUnsafeTheUnsafe.theUnsafe,
+ name, b, off, len, loader, protectionDomain);
+ } catch (Throwable e) {
+ if (e instanceof RuntimeException) throw (RuntimeException) e;
+ if (e instanceof ClassFormatError) throw (ClassFormatError) e;
+ throw new ClassFormatError(e.getMessage());
+ }
+ }
+ }
+
+ private final Object stack;
+ private final Method getCallerClass;
+ private final ReferencedUnsafe sunMiscUnsafe = getReferencedUnsafe();
+
+ Java9 () {
+ Class<?> stackWalkerClass = null;
+ try {
+ stackWalkerClass = Class.forName("java.lang.StackWalker");
+ } catch (ClassNotFoundException e) {
+ // Skip initialization when the class doesn't exist i.e. we are on JDK < 9
+ }
+ if (stackWalkerClass != null) {
+ try {
+ Class<?> optionClass = Class.forName("java.lang.StackWalker$Option");
+ stack = stackWalkerClass.getMethod("getInstance", optionClass)
+ // The first one is RETAIN_CLASS_REFERENCE
+ .invoke(null, optionClass.getEnumConstants()[0]);
+ getCallerClass = stackWalkerClass.getMethod("getCallerClass");
+ } catch (Throwable e) {
+ throw new RuntimeException("cannot initialize", e);
+ }
+ } else {
+ stack = null;
+ getCallerClass = null;
+ }
+ }
+
+ private final ReferencedUnsafe getReferencedUnsafe() {
+ try {
+ if (privileged != null && getCallerClass.invoke(stack) != this.getClass())
+ throw new IllegalAccessError("Access denied for caller.");
+ } catch (Exception e) {
+ throw new RuntimeException("cannot initialize", e);
+ }
+ try {
+ SecurityActions.TheUnsafe usf = SecurityActions.getSunMiscUnsafeAnonymously();
+ List<Method> defineClassMethod = usf.methods.get("defineClass");
+ // On Java 11+ the defineClass method does not exist anymore
+ if (null == defineClassMethod)
+ return null;
+ MethodHandle meth = MethodHandles.lookup().unreflect(defineClassMethod.get(0));
+ return new ReferencedUnsafe(usf, meth);
+ } catch (Throwable e) {
+ throw new RuntimeException("cannot initialize", e);
+ }
+ }
+
+ @Override
+ Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
+ ClassLoader loader, ProtectionDomain protectionDomain)
+ throws ClassFormatError
+ {
+ try {
+ if (getCallerClass.invoke(stack) != DefineClassHelper.class)
+ throw new IllegalAccessError("Access denied for caller.");
+ } catch (Exception e) {
+ throw new RuntimeException("cannot initialize", e);
+ }
+ return sunMiscUnsafe.defineClass(name, b, off, len, loader,
+ protectionDomain);
+ }
+ }
+
+ private static class Java7 extends Helper {
+ private final SecurityActions stack = SecurityActions.stack;
+ private final MethodHandle defineClass = getDefineClassMethodHandle();
+ private final MethodHandle getDefineClassMethodHandle() {
+ if (privileged != null && stack.getCallerClass() != this.getClass())
+ throw new IllegalAccessError("Access denied for caller.");
+ try {
+ return SecurityActions.getMethodHandle(ClassLoader.class, "defineClass",
+ new Class[] {
+ String.class, byte[].class, int.class, int.class,
+ ProtectionDomain.class
+ });
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("cannot initialize", e);
+ }
+ }
+
+ @Override
+ Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
+ ClassLoader loader, ProtectionDomain protectionDomain)
+ throws ClassFormatError
+ {
+ if (stack.getCallerClass() != DefineClassHelper.class)
+ throw new IllegalAccessError("Access denied for caller.");
+ try {
+ return (Class<?>) defineClass.invokeWithArguments(
+ loader, name, b, off, len, protectionDomain);
+ } catch (Throwable e) {
+ if (e instanceof RuntimeException) throw (RuntimeException) e;
+ if (e instanceof ClassFormatError) throw (ClassFormatError) e;
+ throw new ClassFormatError(e.getMessage());
+ }
+ }
+ }
+
+ private static class JavaOther extends Helper {
+ private final Method defineClass = getDefineClassMethod();
+ private final SecurityActions stack = SecurityActions.stack;
+
+ private final Method getDefineClassMethod() {
+ if (privileged != null && stack.getCallerClass() != this.getClass())
+ throw new IllegalAccessError("Access denied for caller.");
+ try {
+ return SecurityActions.getDeclaredMethod(ClassLoader.class, "defineClass",
+ new Class[] {
+ String.class, byte[].class, int.class, int.class, ProtectionDomain.class
+ });
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("cannot initialize", e);
+ }
+ }
+
+ @Override
+ Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
+ ClassLoader loader, ProtectionDomain protectionDomain)
+ throws ClassFormatError, CannotCompileException
+ {
+ Class<?> klass = stack.getCallerClass();
+ if (klass != DefineClassHelper.class && klass != this.getClass())
+ throw new IllegalAccessError("Access denied for caller.");
+ try {
+ SecurityActions.setAccessible(defineClass, true);
+ return (Class<?>) defineClass.invoke(loader, new Object[] {
+ name, b, off, len, protectionDomain
+ });
+ } catch (Throwable e) {
+ if (e instanceof ClassFormatError) throw (ClassFormatError) e;
+ if (e instanceof RuntimeException) throw (RuntimeException) e;
+ throw new CannotCompileException(e);
+ }
+ finally {
+ SecurityActions.setAccessible(defineClass, false);
+ }
+ }
+ }
+
+ // Java 11+ removed sun.misc.Unsafe.defineClass, so we fallback to invoking defineClass on
+ // ClassLoader until we have an implementation that uses MethodHandles.Lookup.defineClass
+ private static final Helper privileged = ClassFile.MAJOR_VERSION > ClassFile.JAVA_10
+ ? new Java11()
+ : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9
+ ? new Java9()
+ : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7 ? new Java7() : new JavaOther();
+
+ /**
+ * Loads a class file by a given class loader.
+ *
+ * <p>This first tries to use {@code java.lang.invoke.MethodHandle} to load a class.
+ * Otherwise, or if {@code neighbor} is null,
+ * this tries to use {@code sun.misc.Unsafe} to load a class.
+ * Then it tries to use a {@code protected} method in {@code java.lang.ClassLoader}
+ * via {@code PrivilegedAction}. Since the latter approach is not available
+ * any longer by default in Java 9 or later, the JVM argument
+ * {@code --add-opens java.base/java.lang=ALL-UNNAMED} must be given to the JVM.
+ * If this JVM argument cannot be given, {@link #toPublicClass(String,byte[])}
+ * should be used instead.
+ * </p>
+ *
+ * @param className the name of the loaded class.
+ * @param neighbor the class contained in the same package as the loaded class.
+ * @param loader the class loader. It can be null if {@code neighbor} is not null
+ * and the JVM is Java 11 or later.
+ * @param domain if it is null, a default domain is used.
+ * @param bcode the bytecode for the loaded class.
+ * @since 3.22
+ */
+ public static Class<?> toClass(String className, Class<?> neighbor, ClassLoader loader,
+ ProtectionDomain domain, byte[] bcode)
+ throws CannotCompileException
+ {
+ try {
+ return privileged.defineClass(className, bcode, 0, bcode.length,
+ neighbor, loader, domain);
+ }
+ catch (RuntimeException e) {
+ throw e;
+ }
+ catch (CannotCompileException e) {
+ throw e;
+ }
+ catch (ClassFormatError e) {
+ Throwable t = e.getCause();
+ throw new CannotCompileException(t == null ? e : t);
+ }
+ catch (Exception e) {
+ throw new CannotCompileException(e);
+ }
+ }
+
+
+ /**
+ * Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}.
+ * It is obtained by using {@code neighbor}.
+ *
+ * @param neighbor a class belonging to the same package that the loaded
+ * class belogns to.
+ * @param bcode the bytecode.
+ * @since 3.24
+ */
+ public static Class<?> toClass(Class<?> neighbor, byte[] bcode)
+ throws CannotCompileException
+ {
+ try {
+ DefineClassHelper.class.getModule().addReads(neighbor.getModule());
+ Lookup lookup = MethodHandles.lookup();
+ Lookup prvlookup = MethodHandles.privateLookupIn(neighbor, lookup);
+ return prvlookup.defineClass(bcode);
+ } catch (IllegalAccessException | IllegalArgumentException e) {
+ throw new CannotCompileException(e.getMessage() + ": " + neighbor.getName()
+ + " has no permission to define the class");
+ }
+ }
+
+ /**
+ * Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}.
+ * It can be obtained by {@code MethodHandles.lookup()} called from
+ * somewhere in the package that the loaded class belongs to.
+ *
+ * @param bcode the bytecode.
+ * @since 3.24
+ */
+ public static Class<?> toClass(Lookup lookup, byte[] bcode)
+ throws CannotCompileException
+ {
+ try {
+ return lookup.defineClass(bcode);
+ } catch (IllegalAccessException | IllegalArgumentException e) {
+ throw new CannotCompileException(e.getMessage());
+ }
+ }
+
+ /**
+ * Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}.
+ *
+ * @since 3.22
+ */
+ static Class<?> toPublicClass(String className, byte[] bcode)
+ throws CannotCompileException
+ {
+ try {
+ Lookup lookup = MethodHandles.lookup();
+ lookup = lookup.dropLookupMode(java.lang.invoke.MethodHandles.Lookup.PRIVATE);
+ return lookup.defineClass(bcode);
+ }
+ catch (Throwable t) {
+ throw new CannotCompileException(t);
+ }
+ }
+
+ private DefineClassHelper() {}
+}
diff --git a/src/main/javassist/util/proxy/DefinePackageHelper.java b/src/main/javassist/util/proxy/DefinePackageHelper.java
new file mode 100644
index 0000000..8a91eb2
--- /dev/null
+++ b/src/main/javassist/util/proxy/DefinePackageHelper.java
@@ -0,0 +1,182 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+
+package javassist.util.proxy;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+
+import javassist.CannotCompileException;
+import javassist.CtClass;
+import javassist.bytecode.ClassFile;
+
+/**
+ * Helper class for invoking {@link ClassLoader#defineClass(String,byte[],int,int)}.
+ *
+ * @since 3.22
+ */
+public class DefinePackageHelper
+{
+ private static abstract class Helper {
+ abstract Package definePackage(ClassLoader loader, String name, String specTitle,
+ String specVersion, String specVendor, String implTitle, String implVersion,
+ String implVendor, URL sealBase)
+ throws IllegalArgumentException;
+ }
+
+ private static class Java9 extends Helper {
+ // definePackage has been discontinued for JAVA 9
+ @Override
+ Package definePackage(ClassLoader loader, String name, String specTitle,
+ String specVersion, String specVendor, String implTitle, String implVersion,
+ String implVendor, URL sealBase)
+ throws IllegalArgumentException
+ {
+ throw new RuntimeException("define package has been disabled for jigsaw");
+ }
+ };
+
+ private static class Java7 extends Helper {
+ private final SecurityActions stack = SecurityActions.stack;
+ private final MethodHandle definePackage = getDefinePackageMethodHandle();
+
+ private MethodHandle getDefinePackageMethodHandle() {
+ if (stack.getCallerClass() != this.getClass())
+ throw new IllegalAccessError("Access denied for caller.");
+ try {
+ return SecurityActions.getMethodHandle(ClassLoader.class,
+ "definePackage", new Class[] {
+ String.class, String.class, String.class, String.class,
+ String.class, String.class, String.class, URL.class
+ });
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("cannot initialize", e);
+ }
+ }
+
+ @Override
+ Package definePackage(ClassLoader loader, String name, String specTitle,
+ String specVersion, String specVendor, String implTitle, String implVersion,
+ String implVendor, URL sealBase)
+ throws IllegalArgumentException
+ {
+ if (stack.getCallerClass() != DefinePackageHelper.class)
+ throw new IllegalAccessError("Access denied for caller.");
+ try {
+ return (Package) definePackage.invokeWithArguments(loader, name, specTitle,
+ specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
+ } catch (Throwable e) {
+ if (e instanceof IllegalArgumentException) throw (IllegalArgumentException) e;
+ if (e instanceof RuntimeException) throw (RuntimeException) e;
+ }
+ return null;
+ }
+ }
+
+ private static class JavaOther extends Helper {
+ private final SecurityActions stack = SecurityActions.stack;
+ private final Method definePackage = getDefinePackageMethod();
+
+ private Method getDefinePackageMethod() {
+ if (stack.getCallerClass() != this.getClass())
+ throw new IllegalAccessError("Access denied for caller.");
+ try {
+ return SecurityActions.getDeclaredMethod(ClassLoader.class,
+ "definePackage", new Class[] {
+ String.class, String.class, String.class, String.class,
+ String.class, String.class, String.class, URL.class
+ });
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("cannot initialize", e);
+ }
+ }
+
+ @Override
+ Package definePackage(ClassLoader loader, String name, String specTitle,
+ String specVersion, String specVendor, String implTitle, String implVersion,
+ String implVendor, URL sealBase)
+ throws IllegalArgumentException
+ {
+ if (stack.getCallerClass() != DefinePackageHelper.class)
+ throw new IllegalAccessError("Access denied for caller.");
+ try {
+ definePackage.setAccessible(true);
+ return (Package) definePackage.invoke(loader, new Object[] {
+ name, specTitle, specVersion, specVendor, implTitle,
+ implVersion, implVendor, sealBase
+ });
+ } catch (Throwable e) {
+ if (e instanceof InvocationTargetException) {
+ Throwable t = ((InvocationTargetException) e).getTargetException();
+ if (t instanceof IllegalArgumentException)
+ throw (IllegalArgumentException) t;
+ }
+ if (e instanceof RuntimeException) throw (RuntimeException) e;
+ }
+ finally {
+ definePackage.setAccessible(false);
+ }
+ return null;
+ }
+ };
+
+ private static final Helper privileged
+ = ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9
+ ? new Java9() : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7
+ ? new Java7() : new JavaOther();
+
+ /**
+ * Defines a new package. If the package is already defined, this method
+ * performs nothing.
+ *
+ * <p>You do not necessarily need to
+ * call this method. If this method is called, then
+ * <code>getPackage()</code> on the <code>Class</code> object returned
+ * by <code>toClass()</code> will return a non-null object.</p>
+ *
+ * <p>The jigsaw module introduced by Java 9 has broken this method.
+ * In Java 9 or later, the VM argument
+ * <code>--add-opens java.base/java.lang=ALL-UNNAMED</code>
+ * has to be given to the JVM so that this method can run.
+ * </p>
+ *
+ * @param loader the class loader passed to <code>toClass()</code> or
+ * the default one obtained by <code>getClassLoader()</code>.
+ * @param className the package name.
+ * @see Class#getClassLoader()
+ * @see CtClass#toClass()
+ */
+ public static void definePackage(String className, ClassLoader loader)
+ throws CannotCompileException
+ {
+ try {
+ privileged.definePackage(loader, className,
+ null, null, null, null, null, null, null);
+ }
+ catch (IllegalArgumentException e) {
+ // if the package is already defined, an IllegalArgumentException
+ // is thrown.
+ return;
+ }
+ catch (Exception e) {
+ throw new CannotCompileException(e);
+ }
+ }
+
+ private DefinePackageHelper() {}
+}
diff --git a/src/main/javassist/util/proxy/FactoryHelper.java b/src/main/javassist/util/proxy/FactoryHelper.java
index 50d1944..1928fdd 100644
--- a/src/main/javassist/util/proxy/FactoryHelper.java
+++ b/src/main/javassist/util/proxy/FactoryHelper.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,7 +16,6 @@
package javassist.util.proxy;
-import java.lang.reflect.Method;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
@@ -34,38 +34,14 @@ import javassist.bytecode.ClassFile;
* @see ProxyFactory
*/
public class FactoryHelper {
- private static java.lang.reflect.Method defineClass1, defineClass2;
-
- static {
- try {
- Class cl = Class.forName("java.lang.ClassLoader");
- defineClass1 = SecurityActions.getDeclaredMethod(
- cl,
- "defineClass",
- new Class[] { String.class, byte[].class,
- int.class, int.class });
-
- defineClass2 = SecurityActions.getDeclaredMethod(
- cl,
- "defineClass",
- new Class[] { String.class, byte[].class,
- int.class, int.class, ProtectionDomain.class });
- }
- catch (Exception e) {
- throw new RuntimeException("cannot initialize");
- }
- }
-
/**
* Returns an index for accessing arrays in this class.
*
* @throws RuntimeException if a given type is not a primitive type.
*/
- public static final int typeIndex(Class type) {
- Class[] list = primitiveTypes;
- int n = list.length;
- for (int i = 0; i < n; i++)
- if (list[i] == type)
+ public static final int typeIndex(Class<?> type) {
+ for (int i = 0; i < primitiveTypes.length; i++)
+ if (primitiveTypes[i] == type)
return i;
throw new RuntimeException("bad type:" + type.getName());
@@ -74,7 +50,7 @@ public class FactoryHelper {
/**
* <code>Class</code> objects representing primitive types.
*/
- public static final Class[] primitiveTypes = {
+ public static final Class<?>[] primitiveTypes = {
Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE,
Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE
};
@@ -126,62 +102,62 @@ public class FactoryHelper {
/**
* Loads a class file by a given class loader.
* This method uses a default protection domain for the class
- * but it may not work with a security manager or a sigend jar file.
+ * but it may not work with a security manager or a signed jar file.
*
- * @see #toClass(ClassFile,ClassLoader,ProtectionDomain)
+ * @see #toClass(ClassFile,Class,ClassLoader,ProtectionDomain)
+ * @deprecated
*/
- public static Class toClass(ClassFile cf, ClassLoader loader)
+ public static Class<?> toClass(ClassFile cf, ClassLoader loader)
throws CannotCompileException
{
- return toClass(cf, loader, null);
+ return toClass(cf, null, loader, null);
}
/**
* Loads a class file by a given class loader.
*
+ * @param neighbor a class belonging to the same package that
+ * the loaded class belongs to.
+ * It can be null.
+ * @param loader The class loader. It can be null if {@code neighbor}
+ * is not null.
* @param domain if it is null, a default domain is used.
* @since 3.3
*/
- public static Class toClass(ClassFile cf, ClassLoader loader, ProtectionDomain domain)
- throws CannotCompileException
+ public static Class<?> toClass(ClassFile cf, Class<?> neighbor,
+ ClassLoader loader, ProtectionDomain domain)
+ throws CannotCompileException
{
try {
byte[] b = toBytecode(cf);
- Method method;
- Object[] args;
- if (domain == null) {
- method = defineClass1;
- args = new Object[] { cf.getName(), b, new Integer(0),
- new Integer(b.length) };
- }
- else {
- method = defineClass2;
- args = new Object[] { cf.getName(), b, new Integer(0),
- new Integer(b.length), domain };
- }
-
- return toClass2(method, loader, args);
- }
- catch (RuntimeException e) {
- throw e;
+ if (ProxyFactory.onlyPublicMethods)
+ return DefineClassHelper.toPublicClass(cf.getName(), b);
+ else
+ return DefineClassHelper.toClass(cf.getName(), neighbor,
+ loader, domain, b);
}
- catch (java.lang.reflect.InvocationTargetException e) {
- throw new CannotCompileException(e.getTargetException());
- }
- catch (Exception e) {
+ catch (IOException e) {
throw new CannotCompileException(e);
}
- }
+ }
- private static synchronized Class toClass2(Method method,
- ClassLoader loader, Object[] args)
- throws Exception
+ /**
+ * Loads a class file by a given lookup.
+ *
+ * @param lookup used to define the class.
+ * @since 3.24
+ */
+ public static Class<?> toClass(ClassFile cf, java.lang.invoke.MethodHandles.Lookup lookup)
+ throws CannotCompileException
{
- SecurityActions.setAccessible(method, true);
- Class clazz = (Class)method.invoke(loader, args);
- SecurityActions.setAccessible(method, false);
- return clazz;
- }
+ try {
+ byte[] b = toBytecode(cf);
+ return DefineClassHelper.toClass(lookup, b);
+ }
+ catch (IOException e) {
+ throw new CannotCompileException(e);
+ }
+ }
private static byte[] toBytecode(ClassFile cf) throws IOException {
ByteArrayOutputStream barray = new ByteArrayOutputStream();
diff --git a/src/main/javassist/util/proxy/MethodFilter.java b/src/main/javassist/util/proxy/MethodFilter.java
index 76084ff..7535e37 100644
--- a/src/main/javassist/util/proxy/MethodFilter.java
+++ b/src/main/javassist/util/proxy/MethodFilter.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
diff --git a/src/main/javassist/util/proxy/MethodHandler.java b/src/main/javassist/util/proxy/MethodHandler.java
index 2bb32cc..2cd8fb1 100644
--- a/src/main/javassist/util/proxy/MethodHandler.java
+++ b/src/main/javassist/util/proxy/MethodHandler.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -21,7 +22,7 @@ import java.lang.reflect.Method;
* The interface implemented by the invocation handler of a proxy
* instance.
*
- * @see ProxyFactory#setHandler(MethodHandler)
+ * @see Proxy#setHandler(MethodHandler)
*/
public interface MethodHandler {
/**
@@ -32,7 +33,7 @@ public interface MethodHandler {
* @param thisMethod the overridden method declared in the super
* class or interface.
* @param proceed the forwarder method for invoking the overridden
- * method. It is null if the overridden mehtod is
+ * method. It is null if the overridden method is
* abstract or declared in the interface.
* @param args an array of objects containing the values of
* the arguments passed in the method invocation
diff --git a/src/main/javassist/util/proxy/Proxy.java b/src/main/javassist/util/proxy/Proxy.java
new file mode 100644
index 0000000..936dd89
--- /dev/null
+++ b/src/main/javassist/util/proxy/Proxy.java
@@ -0,0 +1,33 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+
+package javassist.util.proxy;
+
+/**
+ * The interface implemented by proxy classes.
+ * This interface only provides a setter method.
+ * To obtain a handler, call {@link ProxyFactory#getHandler(Proxy)}.
+ *
+ * @see ProxyFactory
+ * @since 3.16
+ */
+public interface Proxy {
+ /**
+ * Sets a handler. It can be used for changing handlers
+ * during runtime.
+ */
+ void setHandler(MethodHandler mi);
+}
diff --git a/src/main/javassist/util/proxy/ProxyFactory.java b/src/main/javassist/util/proxy/ProxyFactory.java
index 0750950..ae368b2 100644
--- a/src/main/javassist/util/proxy/ProxyFactory.java
+++ b/src/main/javassist/util/proxy/ProxyFactory.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,18 +16,40 @@
package javassist.util.proxy;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
+import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
-import java.util.*;
-import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.lang.invoke.MethodHandles.Lookup;
import javassist.CannotCompileException;
-import javassist.bytecode.*;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.DuplicateMemberException;
+import javassist.bytecode.ExceptionsAttribute;
+import javassist.bytecode.FieldInfo;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+import javassist.bytecode.StackMapTable;
/*
* This class is implemented only with the lower-level API of Javassist.
@@ -44,7 +67,7 @@ import javassist.bytecode.*;
*
* <p>For example, if the following code is executed,
*
- * <ul><pre>
+ * <pre>
* ProxyFactory f = new ProxyFactory();
* f.setSuperclass(Foo.class);
* f.setFilter(new MethodFilter() {
@@ -62,47 +85,49 @@ import javassist.bytecode.*;
* }
* };
* Foo foo = (Foo)c.newInstance();
- * ((ProxyObject)foo).setHandler(mi);
- * </pre></ul>
+ * ((Proxy)foo).setHandler(mi);
+ * </pre>
+ *
+ * <p>Here, <code>Method</code> is <code>java.lang.reflect.Method</code>.</p>
*
* <p>Then, the following method call will be forwarded to MethodHandler
* <code>mi</code> and prints a message before executing the originally called method
* <code>bar()</code> in <code>Foo</code>.
*
- * <ul><pre>
+ * <pre>
* foo.bar();
- * </pre></ul>
+ * </pre>
*
* <p>The last three lines of the code shown above can be replaced with a call to
* the helper method <code>create</code>, which generates a proxy class, instantiates
* it, and sets the method handler of the instance:
*
- * <ul><pre>
+ * <pre>
* :
* Foo foo = (Foo)f.create(new Class[0], new Object[0], mi);
- * </pre></ul>
+ * </pre>
*
* <p>To change the method handler during runtime,
* execute the following code:
*
- * <ul><pre>
+ * <pre>
* MethodHandler mi = ... ; // alternative handler
- * ((ProxyObject)foo).setHandler(mi);
- * </pre></ul>
+ * ((Proxy)foo).setHandler(mi);
+ * </pre>
*
* <p> If setHandler is never called for a proxy instance then it will
* employ the default handler which proceeds by invoking the original method.
* The behaviour of the default handler is identical to the following
* handler:
*
- * <ul><pre>
+ * <pre>
* class EmptyHandler implements MethodHandler {
* public Object invoke(Object self, Method m,
* Method proceed, Object[] args) throws Exception {
* return proceed.invoke(self, args);
* }
* }
- * </pre></ul>
+ * </pre>
*
* <p>A proxy factory caches and reuses proxy classes by default. It is possible to reset
* this default globally by setting static field {@link ProxyFactory#useCache} to false.
@@ -118,7 +143,7 @@ import javassist.bytecode.*;
* with previous releases of javassist. Unfortunately,this legacy behaviour makes caching
* and reuse of proxy classes impossible. The current programming model expects javassist
* clients to set the handler of a proxy instance explicitly by calling method
- * {@link ProxyObject#setHandler(MethodHandler)} as shown in the sample code above. New
+ * {@link Proxy#setHandler(MethodHandler)} as shown in the sample code above. New
* clients are strongly recommended to use this model rather than calling
* {@link ProxyFactory#setHandler(MethodHandler)}.
*
@@ -150,16 +175,17 @@ import javassist.bytecode.*;
* @author Andrew Dinn
*/
public class ProxyFactory {
- private Class superClass;
- private Class[] interfaces;
+ private Class<?> superClass;
+ private Class<?>[] interfaces;
private MethodFilter methodFilter;
private MethodHandler handler; // retained for legacy usage
- private List signatureMethods;
+ private List<Map.Entry<String,Method>> signatureMethods;
+ private boolean hasGetHandler;
private byte[] signature;
private String classname;
private String basename;
private String superName;
- private Class thisClass;
+ private Class<?> thisClass;
/**
* per factory setting initialised from current setting for useCache but able to be reset before each create call
*/
@@ -169,6 +195,29 @@ public class ProxyFactory {
*/
private boolean factoryWriteReplace;
+ /**
+ * <p>If true, only public/protected methods are forwarded to a proxy object.
+ * The class for that proxy object is loaded by the {@code defineClass} method
+ * in {@code java.lang.invoke.MethodHandles.Lookup}, which is available in
+ * Java 9 or later. This works even when {@code sun.misc.Unsafe} is not
+ * available for some reasons (it is already deprecated in Java 9).</p>
+ *
+ * <p>To load a class, Javassist first tries to use {@code sun.misc.Unsafe} and,
+ * if not available, it uses a {@code protected} method in {@code java.lang.ClassLoader}
+ * via {@code PrivilegedAction}. Since the latter approach is not available
+ * any longer by default in Java 9 or later, the JVM argument
+ * {@code --add-opens java.base/java.lang=ALL-UNNAMED} must be given to the JVM
+ * when it is used (because of lack of {@code sun.misc.Unsafe}).
+ * If this argument cannot be given to the JVM, {@code onlyPublicMethods} should
+ * be set to {@code true}. Javassist will try to load by using
+ * {@code java.lang.invoke.MethodHandles.Lookup}.</p>
+ *
+ * <p>The default value is {@code false}.</p>
+ *
+ * @see DefineClassHelper#toClass(String, Class, ClassLoader, ProtectionDomain, byte[])
+ * @since 3.22
+ */
+ public static boolean onlyPublicMethods = false;
/**
* If the value of this variable is not null, the class file of
@@ -181,7 +230,7 @@ public class ProxyFactory {
*/
public String writeDirectory;
- private static final Class OBJECT_TYPE = Object.class;
+ private static final Class<?> OBJECT_TYPE = Object.class;
private static final String HOLDER = "_methods_";
private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;";
@@ -200,7 +249,7 @@ public class ProxyFactory {
private static final String SERIAL_VERSION_UID_FIELD = "serialVersionUID";
private static final String SERIAL_VERSION_UID_TYPE = "J";
- private static final int SERIAL_VERSION_UID_VALUE = -1;
+ private static final long SERIAL_VERSION_UID_VALUE = -1L;
/**
* If true, a generated proxy class is cached and it will be reused
@@ -283,17 +332,18 @@ public class ProxyFactory {
factoryWriteReplace = useWriteReplace;
}
- private static WeakHashMap proxyCache = new WeakHashMap();
+ private static Map<ClassLoader,Map<String,ProxyDetails>> proxyCache =
+ new WeakHashMap<ClassLoader,Map<String,ProxyDetails>>();
/**
* determine if a class is a javassist proxy class
* @param cl
* @return true if the class is a javassist proxy class otherwise false
*/
- public static boolean isProxyClass(Class cl)
+ public static boolean isProxyClass(Class<?> cl)
{
- // all proxies implement ProxyObject. nothing else should.
- return (ProxyObject.class.isAssignableFrom(cl));
+ // all proxies implement Proxy or ProxyObject. nothing else should.
+ return (Proxy.class.isAssignableFrom(cl));
}
/**
@@ -312,17 +362,17 @@ public class ProxyFactory {
* a hexadecimal string representation of the signature bit sequence. this string also forms part
* of the proxy class name.
*/
- WeakReference proxyClass;
+ Reference<Class<?>> proxyClass;
/**
* a flag which is true this class employs writeReplace to perform serialization of its instances
* and false if serialization must employ of a ProxyObjectOutputStream and ProxyObjectInputStream
*/
boolean isUseWriteReplace;
- ProxyDetails(byte[] signature, Class proxyClass, boolean isUseWriteReplace)
+ ProxyDetails(byte[] signature, Class<?> proxyClass, boolean isUseWriteReplace)
{
this.signature = signature;
- this.proxyClass = new WeakReference(proxyClass);
+ this.proxyClass = new WeakReference<Class<?>>(proxyClass);
this.isUseWriteReplace = isUseWriteReplace;
}
}
@@ -337,6 +387,7 @@ public class ProxyFactory {
handler = null;
signature = null;
signatureMethods = null;
+ hasGetHandler = false;
thisClass = null;
writeDirectory = null;
factoryUseCache = useCache;
@@ -346,7 +397,7 @@ public class ProxyFactory {
/**
* Sets the super class of a proxy class.
*/
- public void setSuperclass(Class clazz) {
+ public void setSuperclass(Class<?> clazz) {
superClass = clazz;
// force recompute of signature
signature = null;
@@ -357,12 +408,12 @@ public class ProxyFactory {
*
* @since 3.4
*/
- public Class getSuperclass() { return superClass; }
+ public Class<?> getSuperclass() { return superClass; }
/**
* Sets the interfaces of a proxy class.
*/
- public void setInterfaces(Class[] ifs) {
+ public void setInterfaces(Class<?>[] ifs) {
interfaces = ifs;
// force recompute of signature
signature = null;
@@ -373,7 +424,7 @@ public class ProxyFactory {
*
* @since 3.4
*/
- public Class[] getInterfaces() { return interfaces; }
+ public Class<?>[] getInterfaces() { return interfaces; }
/**
* Sets a filter that selects the methods that will be controlled by a handler.
@@ -386,49 +437,99 @@ public class ProxyFactory {
/**
* Generates a proxy class using the current filter.
+ * The module or package where a proxy class is created
+ * has to be opened to this package or the Javassist module.
+ *
+ * @see #createClass(Lookup)
*/
- public Class createClass() {
+ public Class<?> createClass() {
if (signature == null) {
computeSignature(methodFilter);
}
- return createClass1();
+ return createClass1(null);
}
/**
* Generates a proxy class using the supplied filter.
+ * The module or package where a proxy class is created
+ * has to be opened to this package or the Javassist module.
*/
- public Class createClass(MethodFilter filter) {
+ public Class<?> createClass(MethodFilter filter) {
computeSignature(filter);
- return createClass1();
+ return createClass1(null);
}
/**
* Generates a proxy class with a specific signature.
* access is package local so ProxyObjectInputStream can use this
* @param signature
- * @return
*/
- Class createClass(byte[] signature)
+ Class<?> createClass(byte[] signature)
{
installSignature(signature);
- return createClass1();
+ return createClass1(null);
}
- private Class createClass1() {
- if (thisClass == null) {
+ /**
+ * Generates a proxy class using the current filter.
+ *
+ * @param lookup used for loading the proxy class.
+ * It needs an appropriate right to invoke {@code defineClass}
+ * for the proxy class.
+ * @since 3.24
+ */
+ public Class<?> createClass(Lookup lookup) {
+ if (signature == null) {
+ computeSignature(methodFilter);
+ }
+ return createClass1(lookup);
+ }
+
+ /**
+ * Generates a proxy class using the supplied filter.
+ *
+ * @param lookup used for loading the proxy class.
+ * It needs an appropriate right to invoke {@code defineClass}
+ * for the proxy class.
+ * @param filter the filter.
+ * @since 3.24
+ */
+ public Class<?> createClass(Lookup lookup, MethodFilter filter) {
+ computeSignature(filter);
+ return createClass1(lookup);
+ }
+
+ /**
+ * Generates a proxy class with a specific signature.
+ * access is package local so ProxyObjectInputStream can use this.
+ *
+ * @param lookup used for loading the proxy class.
+ * It needs an appropriate right to invoke {@code defineClass}
+ * for the proxy class.
+ * @param signature the signature.
+ */
+ Class<?> createClass(Lookup lookup, byte[] signature)
+ {
+ installSignature(signature);
+ return createClass1(lookup);
+ }
+
+ private Class<?> createClass1(Lookup lookup) {
+ Class<?> result = thisClass;
+ if (result == null) {
ClassLoader cl = getClassLoader();
synchronized (proxyCache) {
if (factoryUseCache)
- createClass2(cl);
- else
- createClass3(cl);
+ createClass2(cl, lookup);
+ else
+ createClass3(cl, lookup);
+
+ result = thisClass;
+ // don't retain any unwanted references
+ thisClass = null;
}
}
- // don't retain any unwanted references
- Class result = thisClass;
- thisClass = null;
-
return result;
}
@@ -436,7 +537,7 @@ public class ProxyFactory {
{ '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- public String getKey(Class superClass, Class[] interfaces, byte[] signature, boolean useWriteReplace)
+ public String getKey(Class<?> superClass, Class<?>[] interfaces, byte[] signature, boolean useWriteReplace)
{
StringBuffer sbuf = new StringBuffer();
if (superClass != null){
@@ -461,7 +562,7 @@ public class ProxyFactory {
return sbuf.toString();
}
- private void createClass2(ClassLoader cl) {
+ private void createClass2(ClassLoader cl, Lookup lookup) {
String key = getKey(superClass, interfaces, signature, factoryWriteReplace);
/*
* Excessive concurrency causes a large memory footprint and slows the
@@ -469,27 +570,27 @@ public class ProxyFactory {
* reducing concrrency.
*/
// synchronized (proxyCache) {
- HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl);
+ Map<String,ProxyDetails> cacheForTheLoader = proxyCache.get(cl);
ProxyDetails details;
if (cacheForTheLoader == null) {
- cacheForTheLoader = new HashMap();
+ cacheForTheLoader = new HashMap<String,ProxyDetails>();
proxyCache.put(cl, cacheForTheLoader);
}
- details = (ProxyDetails)cacheForTheLoader.get(key);
+ details = cacheForTheLoader.get(key);
if (details != null) {
- WeakReference reference = details.proxyClass;
- thisClass = (Class)reference.get();
+ Reference<Class<?>> reference = details.proxyClass;
+ thisClass = reference.get();
if (thisClass != null) {
return;
}
}
- createClass3(cl);
+ createClass3(cl, lookup);
details = new ProxyDetails(signature, thisClass, factoryWriteReplace);
cacheForTheLoader.put(key, details);
// }
}
- private void createClass3(ClassLoader cl) {
+ private void createClass3(ClassLoader cl, Lookup lookup) {
// we need a new class so we need a new class name
allocateClassName();
@@ -498,7 +599,11 @@ public class ProxyFactory {
if (writeDirectory != null)
FactoryHelper.writeFile(cf, writeDirectory);
- thisClass = FactoryHelper.toClass(cf, cl, getDomain());
+ if (lookup == null)
+ thisClass = FactoryHelper.toClass(cf, getClassInTheSamePackage(), cl, getDomain());
+ else
+ thisClass = FactoryHelper.toClass(cf, lookup);
+
setField(FILTER_SIGNATURE_FIELD, signature);
// legacy behaviour : we only set the default interceptor static field if we are not using the cache
if (!factoryUseCache) {
@@ -511,6 +616,22 @@ public class ProxyFactory {
}
+ /**
+ * Obtains a class belonging to the same package that the created
+ * proxy class belongs to. It is used to obtain an appropriate
+ * {@code java.lang.invoke.MethodHandles.Lookup}.
+ */
+ private Class<?> getClassInTheSamePackage() {
+ if (basename.startsWith(packageForJavaBase)) // maybe the super class is java.*
+ return this.getClass();
+ else if (superClass != null && superClass != OBJECT_TYPE)
+ return superClass;
+ else if (interfaces != null && interfaces.length > 0)
+ return interfaces[0];
+ else
+ return this.getClass(); // maybe wrong?
+ }
+
private void setField(String fieldName, Object value) {
if (thisClass != null && value != null)
try {
@@ -524,11 +645,11 @@ public class ProxyFactory {
}
}
- static byte[] getFilterSignature(Class clazz) {
+ static byte[] getFilterSignature(Class<?> clazz) {
return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD);
}
- private static Object getField(Class clazz, String fieldName) {
+ private static Object getField(Class<?> clazz, String fieldName) {
try {
Field f = clazz.getField(fieldName);
f.setAccessible(true);
@@ -542,6 +663,26 @@ public class ProxyFactory {
}
/**
+ * Obtains the method handler of the given proxy object.
+ *
+ * @param p a proxy object.
+ * @return the method handler.
+ * @since 3.16
+ */
+ public static MethodHandler getHandler(Proxy p) {
+ try {
+ Field f = p.getClass().getDeclaredField(HANDLER);
+ f.setAccessible(true);
+ Object value = f.get(p);
+ f.setAccessible(false);
+ return (MethodHandler)value;
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* A provider of class loaders.
*
* @see #classLoaderProvider
@@ -566,22 +707,23 @@ public class ProxyFactory {
* implementation.
*
* <p>Example:
- * <ul><pre>
+ * <pre>
* ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
* public ClassLoader get(ProxyFactory pf) {
* return Thread.currentThread().getContextClassLoader();
* }
* };
- * </pre></ul>
+ * </pre>
*
* @since 3.4
*/
- public static ClassLoaderProvider classLoaderProvider
- = new ClassLoaderProvider() {
- public ClassLoader get(ProxyFactory pf) {
+ public static ClassLoaderProvider classLoaderProvider =
+ new ClassLoaderProvider() {
+ @Override
+ public ClassLoader get(ProxyFactory pf) {
return pf.getClassLoader0();
}
- };
+ };
protected ClassLoader getClassLoader() {
return classLoaderProvider.get(this);
@@ -608,7 +750,7 @@ public class ProxyFactory {
}
protected ProtectionDomain getDomain() {
- Class clazz;
+ Class<?> clazz;
if (superClass != null && !superClass.getName().equals("java.lang.Object"))
clazz = superClass;
else if (interfaces != null && interfaces.length > 0)
@@ -627,12 +769,12 @@ public class ProxyFactory {
* @param mh the method handler for the proxy class.
* @since 3.4
*/
- public Object create(Class[] paramTypes, Object[] args, MethodHandler mh)
+ public Object create(Class<?>[] paramTypes, Object[] args, MethodHandler mh)
throws NoSuchMethodException, IllegalArgumentException,
InstantiationException, IllegalAccessException, InvocationTargetException
{
Object obj = create(paramTypes, args);
- ((ProxyObject)obj).setHandler(mh);
+ ((Proxy)obj).setHandler(mh);
return obj;
}
@@ -642,12 +784,12 @@ public class ProxyFactory {
* @param paramTypes parameter types for a constructor.
* @param args arguments passed to a constructor.
*/
- public Object create(Class[] paramTypes, Object[] args)
+ public Object create(Class<?>[] paramTypes, Object[] args)
throws NoSuchMethodException, IllegalArgumentException,
InstantiationException, IllegalAccessException, InvocationTargetException
{
- Class c = createClass();
- Constructor cons = c.getConstructor(paramTypes);
+ Class<?> c = createClass();
+ Constructor<?> cons = c.getConstructor(paramTypes);
return cons.newInstance(args);
}
@@ -657,10 +799,11 @@ public class ProxyFactory {
* specified.
* @deprecated since 3.12
* use of this method is incompatible with proxy class caching.
- * instead clients should call method {@link ProxyObject#setHandler(MethodHandler)} to set the handler
+ * instead clients should call method {@link Proxy#setHandler(MethodHandler)} to set the handler
* for each newly created proxy instance.
* calling this method will automatically disable caching of classes created by the proxy factory.
*/
+ @Deprecated
public void setHandler(MethodHandler mi) {
// if we were using the cache and the handler is non-null then we must stop caching
if (factoryUseCache && mi != null) {
@@ -674,16 +817,45 @@ public class ProxyFactory {
setField(DEFAULT_INTERCEPTOR, handler);
}
- private static int counter = 0;
+ /**
+ * A unique class name generator.
+ */
+ public static interface UniqueName {
+ /**
+ * Returns a unique class name.
+ *
+ * @param classname the super class name of the proxy class.
+ */
+ String get(String classname);
+ }
+
+ /**
+ * A unique class name generator.
+ * Replacing this generator changes the algorithm to generate a
+ * unique name. The <code>get</code> method does not have to be
+ * a <code>synchronized</code> method since the access to this field
+ * is mutually exclusive and thus thread safe.
+ */
+ public static UniqueName nameGenerator = new UniqueName() {
+ private final String sep = "_$$_jvst" + Integer.toHexString(this.hashCode() & 0xfff) + "_";
+ private int counter = 0;
- private static synchronized String makeProxyName(String classname) {
- return classname + "_$$_javassist_" + counter++;
+ @Override
+ public String get(String classname) {
+ return classname + sep + Integer.toHexString(counter++);
+ }
+ };
+
+ private static String makeProxyName(String classname) {
+ synchronized (nameGenerator) {
+ return nameGenerator.get(classname);
+ }
}
private ClassFile make() throws CannotCompileException {
ClassFile cf = new ClassFile(false, classname, superName);
cf.setAccessFlags(AccessFlag.PUBLIC);
- setInterfaces(cf, interfaces);
+ setInterfaces(cf, interfaces, hasGetHandler ? Proxy.class : ProxyObject.class);
ConstPool pool = cf.getConstPool();
// legacy: we only add the static field for the default interceptor if caching is disabled
@@ -707,14 +879,17 @@ public class ProxyFactory {
FieldInfo finfo4 = new FieldInfo(pool, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE);
finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL);
cf.addField(finfo4);
-
+
// HashMap allMethods = getMethods(superClass, interfaces);
// int size = allMethods.size();
makeConstructors(classname, cf, pool, classname);
- int s = overrideMethods(cf, pool, classname);
- addMethodsHolder(cf, pool, classname, s);
+
+ List<Find2MethodsArgs> forwarders = new ArrayList<Find2MethodsArgs>();
+ int s = overrideMethods(cf, pool, classname, forwarders);
+ addClassInitializer(cf, pool, classname, s, forwarders);
addSetter(classname, cf, pool);
- addGetter(classname, cf, pool);
+ if (!hasGetHandler)
+ addGetter(classname, cf, pool);
if (factoryWriteReplace) {
try {
@@ -729,8 +904,7 @@ public class ProxyFactory {
return cf;
}
- private void checkClassAndSuperName()
- {
+ private void checkClassAndSuperName() {
if (interfaces == null)
interfaces = new Class[0];
@@ -738,7 +912,7 @@ public class ProxyFactory {
superClass = OBJECT_TYPE;
superName = superClass.getName();
basename = interfaces.length == 0 ? superName
- : interfaces[0].getName();
+ : interfaces[0].getName();
} else {
superName = superClass.getName();
basename = superName;
@@ -746,33 +920,34 @@ public class ProxyFactory {
if (Modifier.isFinal(superClass.getModifiers()))
throw new RuntimeException(superName + " is final");
-
- if (basename.startsWith("java."))
- basename = "org.javassist.tmp." + basename;
+
+ // Since java.base module is not opened, its proxy class should be
+ // in a different (open) module. Otherwise, it could not be created
+ // by reflection.
+ if (basename.startsWith("java.") || basename.startsWith("jdk.") || onlyPublicMethods)
+ basename = packageForJavaBase + basename.replace('.', '_');
}
- private void allocateClassName()
- {
+ private static final String packageForJavaBase = "javassist.util.proxy.";
+
+ private void allocateClassName() {
classname = makeProxyName(basename);
}
- private static Comparator sorter = new Comparator() {
-
- public int compare(Object o1, Object o2) {
- Map.Entry e1 = (Map.Entry)o1;
- Map.Entry e2 = (Map.Entry)o2;
- String key1 = (String)e1.getKey();
- String key2 = (String)e2.getKey();
- return key1.compareTo(key2);
- }
- };
+ private static Comparator<Map.Entry<String,Method>> sorter =
+ new Comparator<Map.Entry<String,Method>>() {
+ @Override
+ public int compare(Map.Entry<String,Method> e1, Map.Entry<String,Method> e2) {
+ return e1.getKey().compareTo(e2.getKey());
+ }
+ };
- private void makeSortedMethodList()
- {
+ private void makeSortedMethodList() {
checkClassAndSuperName();
- HashMap allMethods = getMethods(superClass, interfaces);
- signatureMethods = new ArrayList(allMethods.entrySet());
+ hasGetHandler = false; // getMethods() may set this to true.
+ Map<String,Method> allMethods = getMethods(superClass, interfaces);
+ signatureMethods = new ArrayList<Map.Entry<String,Method>>(allMethods.entrySet());
Collections.sort(signatureMethods, sorter);
}
@@ -785,8 +960,7 @@ public class ProxyFactory {
signature = new byte[maxBytes];
for (int idx = 0; idx < l; idx++)
{
- Map.Entry e = (Map.Entry)signatureMethods.get(idx);
- Method m = (Method)e.getValue();
+ Method m = signatureMethods.get(idx).getValue();
int mod = m.getModifiers();
if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod)
&& isVisible(mod, basename, m) && (filter == null || filter.isHandled(m))) {
@@ -808,21 +982,17 @@ public class ProxyFactory {
this.signature = signature;
}
- private boolean testBit(byte[] signature, int idx)
- {
+ private boolean testBit(byte[] signature, int idx) {
int byteIdx = idx >> 3;
- if (byteIdx > signature.length) {
+ if (byteIdx > signature.length)
return false;
- } else {
- int bitIdx = idx & 0x7;
- int mask = 0x1 << bitIdx;
- int sigByte = signature[byteIdx];
- return ((sigByte & mask) != 0);
- }
+ int bitIdx = idx & 0x7;
+ int mask = 0x1 << bitIdx;
+ int sigByte = signature[byteIdx];
+ return ((sigByte & mask) != 0);
}
- private void setBit(byte[] signature, int idx)
- {
+ private void setBit(byte[] signature, int idx) {
int byteIdx = idx >> 3;
if (byteIdx < signature.length) {
int bitIdx = idx & 0x7;
@@ -832,8 +1002,8 @@ public class ProxyFactory {
}
}
- private static void setInterfaces(ClassFile cf, Class[] interfaces) {
- String setterIntf = ProxyObject.class.getName();
+ private static void setInterfaces(ClassFile cf, Class<?>[] interfaces, Class<?> proxyClass) {
+ String setterIntf = proxyClass.getName();
String[] list;
if (interfaces == null || interfaces.length == 0)
list = new String[] { setterIntf };
@@ -848,8 +1018,8 @@ public class ProxyFactory {
cf.setInterfaces(list);
}
- private static void addMethodsHolder(ClassFile cf, ConstPool cp,
- String classname, int size)
+ private static void addClassInitializer(ClassFile cf, ConstPool cp,
+ String classname, int size, List<Find2MethodsArgs> forwarders)
throws CannotCompileException
{
FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE);
@@ -857,18 +1027,58 @@ public class ProxyFactory {
cf.addField(finfo);
MethodInfo minfo = new MethodInfo(cp, "<clinit>", "()V");
minfo.setAccessFlags(AccessFlag.STATIC);
- Bytecode code = new Bytecode(cp, 0, 0);
+ setThrows(minfo, cp, new Class<?>[] { ClassNotFoundException.class });
+
+ Bytecode code = new Bytecode(cp, 0, 2);
code.addIconst(size * 2);
code.addAnewarray("java.lang.reflect.Method");
+ final int varArray = 0;
+ code.addAstore(varArray);
+
+ // forName() must be called here. Otherwise, the class might be
+ // invisible.
+ code.addLdc(classname);
+ code.addInvokestatic("java.lang.Class",
+ "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
+ final int varClass = 1;
+ code.addAstore(varClass);
+
+ for (Find2MethodsArgs args:forwarders)
+ callFind2Methods(code, args.methodName, args.delegatorName,
+ args.origIndex, args.descriptor, varClass, varArray);
+
+ code.addAload(varArray);
code.addPutstatic(classname, HOLDER, HOLDER_TYPE);
- // also need to set serial version uid
- code.addLconst(-1L);
+
+ code.addLconst(SERIAL_VERSION_UID_VALUE);
code.addPutstatic(classname, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE);
code.addOpcode(Bytecode.RETURN);
minfo.setCodeAttribute(code.toCodeAttribute());
cf.addMethod(minfo);
}
+ /**
+ * @param thisMethod might be null.
+ */
+ private static void callFind2Methods(Bytecode code, String superMethod, String thisMethod,
+ int index, String desc, int classVar, int arrayVar) {
+ String findClass = RuntimeSupport.class.getName();
+ String findDesc
+ = "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/reflect/Method;)V";
+
+ code.addAload(classVar);
+ code.addLdc(superMethod);
+ if (thisMethod == null)
+ code.addOpcode(Opcode.ACONST_NULL);
+ else
+ code.addLdc(thisMethod);
+
+ code.addIconst(index);
+ code.addLdc(desc);
+ code.addAload(arrayVar);
+ code.addInvokestatic(findClass, "find2Methods", findDesc);
+ }
+
private static void addSetter(String classname, ClassFile cf, ConstPool cp)
throws CannotCompileException
{
@@ -898,32 +1108,36 @@ public class ProxyFactory {
cf.addMethod(minfo);
}
- private int overrideMethods(ClassFile cf, ConstPool cp, String className)
+ private int overrideMethods(ClassFile cf, ConstPool cp, String className, List<Find2MethodsArgs> forwarders)
throws CannotCompileException
{
String prefix = makeUniqueName("_d", signatureMethods);
- Iterator it = signatureMethods.iterator();
+ Iterator<Map.Entry<String,Method>> it = signatureMethods.iterator();
int index = 0;
while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- String key = (String)e.getKey();
- Method meth = (Method)e.getValue();
- int mod = meth.getModifiers();
- if (testBit(signature, index)) {
- override(className, meth, prefix, index,
- keyToDesc(key), cf, cp);
- }
+ Map.Entry<String,Method> e = it.next();
+ if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_5 || !isBridge(e.getValue()))
+ if (testBit(signature, index)) {
+ override(className, e.getValue(), prefix, index,
+ keyToDesc(e.getKey(), e.getValue()), cf, cp, forwarders);
+ }
+
index++;
}
return index;
}
+ private static boolean isBridge(Method m) {
+ return m.isBridge();
+ }
+
private void override(String thisClassname, Method meth, String prefix,
- int index, String desc, ClassFile cf, ConstPool cp)
+ int index, String desc, ClassFile cf, ConstPool cp,
+ List<Find2MethodsArgs> forwarders)
throws CannotCompileException
{
- Class declClass = meth.getDeclaringClass();
+ Class<?> declClass = meth.getDeclaringClass();
String delegatorName = prefix + index + meth.getName();
if (Modifier.isAbstract(meth.getModifiers()))
delegatorName = null;
@@ -937,18 +1151,18 @@ public class ProxyFactory {
MethodInfo forwarder
= makeForwarder(thisClassname, meth, desc, cp, declClass,
- delegatorName, index);
+ delegatorName, index, forwarders);
cf.addMethod(forwarder);
}
private void makeConstructors(String thisClassName, ClassFile cf,
ConstPool cp, String classname) throws CannotCompileException
{
- Constructor[] cons = SecurityActions.getDeclaredConstructors(superClass);
+ Constructor<?>[] cons = SecurityActions.getDeclaredConstructors(superClass);
// legacy: if we are not caching then we need to initialise the default handler
boolean doHandlerInit = !factoryUseCache;
for (int i = 0; i < cons.length; i++) {
- Constructor c = cons[i];
+ Constructor<?> c = cons[i];
int mod = c.getModifiers();
if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod)
&& isVisible(mod, basename, c)) {
@@ -958,7 +1172,7 @@ public class ProxyFactory {
}
}
- private static String makeUniqueName(String name, List sortedMethods) {
+ private static String makeUniqueName(String name, List<Map.Entry<String,Method>> sortedMethods) {
if (makeUniqueName0(name, sortedMethods.iterator()))
return name;
@@ -971,14 +1185,10 @@ public class ProxyFactory {
throw new RuntimeException("cannot make a unique method name");
}
- private static boolean makeUniqueName0(String name, Iterator it) {
- while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- String key = (String)e.getKey();
- if (key.startsWith(name))
+ private static boolean makeUniqueName0(String name, Iterator<Map.Entry<String,Method>> it) {
+ while (it.hasNext())
+ if (it.next().getKey().startsWith(name))
return false;
- }
-
return true;
}
@@ -997,8 +1207,7 @@ public class ProxyFactory {
String q = getPackageName(meth.getDeclaringClass().getName());
if (p == null)
return q == null;
- else
- return p.equals(q);
+ return p.equals(q);
}
}
@@ -1006,40 +1215,62 @@ public class ProxyFactory {
int i = name.lastIndexOf('.');
if (i < 0)
return null;
- else
- return name.substring(0, i);
+ return name.substring(0, i);
}
- private static HashMap getMethods(Class superClass, Class[] interfaceTypes) {
- HashMap hash = new HashMap();
+ /* getMethods() may set hasGetHandler to true.
+ */
+ private Map<String,Method> getMethods(Class<?> superClass, Class<?>[] interfaceTypes) {
+ Map<String,Method> hash = new HashMap<String,Method>();
+ Set<Class<?>> set = new HashSet<Class<?>>();
for (int i = 0; i < interfaceTypes.length; i++)
- getMethods(hash, interfaceTypes[i]);
+ getMethods(hash, interfaceTypes[i], set);
- getMethods(hash, superClass);
+ getMethods(hash, superClass, set);
return hash;
}
- private static void getMethods(HashMap hash, Class clazz) {
- Class[] ifs = clazz.getInterfaces();
+ private void getMethods(Map<String,Method> hash, Class<?> clazz, Set<Class<?>> visitedClasses) {
+ // This both speeds up scanning by avoiding duplicate interfaces and is needed to
+ // ensure that superinterfaces are always scanned before subinterfaces.
+ if (!visitedClasses.add(clazz))
+ return;
+
+ Class<?>[] ifs = clazz.getInterfaces();
for (int i = 0; i < ifs.length; i++)
- getMethods(hash, ifs[i]);
+ getMethods(hash, ifs[i], visitedClasses);
- Class parent = clazz.getSuperclass();
+ Class<?> parent = clazz.getSuperclass();
if (parent != null)
- getMethods(hash, parent);
+ getMethods(hash, parent, visitedClasses);
+ /* Java 5 or later allows covariant return types.
+ * It also allows contra-variant parameter types
+ * if a super class is a generics with concrete type arguments
+ * such as Foo<String>. So the method-overriding rule is complex.
+ */
Method[] methods = SecurityActions.getDeclaredMethods(clazz);
for (int i = 0; i < methods.length; i++)
if (!Modifier.isPrivate(methods[i].getModifiers())) {
Method m = methods[i];
- String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m);
+ String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m); // see keyToDesc().
+ if (key.startsWith(HANDLER_GETTER_KEY))
+ hasGetHandler = true;
+
// JIRA JASSIST-85
- // put the method to the cache, retrieve previous definition (if any)
- Method oldMethod = (Method)hash.put(key, methods[i]);
+ // put the method to the cache, retrieve previous definition (if any)
+ Method oldMethod = hash.put(key, m);
+
+ // JIRA JASSIST-244, 267
+ // ignore a bridge method to a method declared in a non-public class.
+ if (null != oldMethod && isBridge(m)
+ && !Modifier.isPublic(oldMethod.getDeclaringClass().getModifiers())
+ && !Modifier.isAbstract(oldMethod.getModifiers()) && !isDuplicated(i, methods))
+ hash.put(key, oldMethod);
// check if visibility has been reduced
if (null != oldMethod && Modifier.isPublic(oldMethod.getModifiers())
- && !Modifier.isPublic(methods[i].getModifiers()) ) {
+ && !Modifier.isPublic(m.getModifiers())) {
// we tried to overwrite a public definition with a non-public definition,
// use the old definition instead.
hash.put(key, oldMethod);
@@ -1047,12 +1278,41 @@ public class ProxyFactory {
}
}
- private static String keyToDesc(String key) {
+ private static boolean isDuplicated(int index, Method[] methods) {
+ String name = methods[index].getName();
+ for (int i = 0; i < methods.length; i++)
+ if (i != index)
+ if (name.equals(methods[i].getName()) && areParametersSame(methods[index], methods[i]))
+ return true;
+
+ return false;
+ }
+
+ private static boolean areParametersSame(Method method, Method targetMethod) {
+ Class<?>[] methodTypes = method.getParameterTypes();
+ Class<?>[] targetMethodTypes = targetMethod.getParameterTypes();
+ if (methodTypes.length == targetMethodTypes.length) {
+ for (int i = 0; i< methodTypes.length; i++) {
+ if (methodTypes[i].getName().equals(targetMethodTypes[i].getName())) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private static final String HANDLER_GETTER_KEY
+ = HANDLER_GETTER + ":()";
+
+ private static String keyToDesc(String key, Method m) {
return key.substring(key.indexOf(':') + 1);
}
- private static MethodInfo makeConstructor(String thisClassName, Constructor cons,
- ConstPool cp, Class superClass, boolean doHandlerInit) {
+ private static MethodInfo makeConstructor(String thisClassName, Constructor<?> cons,
+ ConstPool cp, Class<?> superClass, boolean doHandlerInit) {
String desc = RuntimeSupport.makeDescriptor(cons.getParameterTypes(),
Void.TYPE);
MethodInfo minfo = new MethodInfo(cp, "<init>", desc);
@@ -1092,8 +1352,8 @@ public class ProxyFactory {
return minfo;
}
- private static MethodInfo makeDelegator(Method meth, String desc,
- ConstPool cp, Class declClass, String delegatorName) {
+ private MethodInfo makeDelegator(Method meth, String desc,
+ ConstPool cp, Class<?> declClass, String delegatorName) {
MethodInfo delegator = new MethodInfo(cp, delegatorName, desc);
delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC
| (meth.getModifiers() & ~(Modifier.PRIVATE
@@ -1105,19 +1365,36 @@ public class ProxyFactory {
Bytecode code = new Bytecode(cp, 0, 0);
code.addAload(0);
int s = addLoadParameters(code, meth.getParameterTypes(), 1);
- code.addInvokespecial(declClass.getName(), meth.getName(), desc);
+ Class<?> targetClass = invokespecialTarget(declClass);
+ code.addInvokespecial(targetClass.isInterface(), cp.addClassInfo(targetClass.getName()),
+ meth.getName(), desc);
addReturn(code, meth.getReturnType());
code.setMaxLocals(++s);
delegator.setCodeAttribute(code.toCodeAttribute());
return delegator;
}
+ /* Suppose that the receiver type is S, the invoked method
+ * is declared in T, and U is the immediate super class of S
+ * (or its interface). If S <: U <: T (S <: T reads "S extends T"),
+ * the target type of invokespecial has to be not T but U.
+ */
+ private Class<?> invokespecialTarget(Class<?> declClass) {
+ if (declClass.isInterface())
+ for (Class<?> i: interfaces)
+ if (declClass.isAssignableFrom(i))
+ return i;
+
+ return superClass;
+ }
+
/**
* @param delegatorName null if the original method is abstract.
*/
private static MethodInfo makeForwarder(String thisClassName,
Method meth, String desc, ConstPool cp,
- Class declClass, String delegatorName, int index) {
+ Class<?> declClass, String delegatorName, int index,
+ List<Find2MethodsArgs> forwarders) {
MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc);
forwarder.setAccessFlags(Modifier.FINAL
| (meth.getModifiers() & ~(Modifier.ABSTRACT
@@ -1127,13 +1404,14 @@ public class ProxyFactory {
int args = Descriptor.paramSize(desc);
Bytecode code = new Bytecode(cp, 0, args + 2);
/*
- * if (methods[index * 2] == null) {
+ * static {
* methods[index * 2]
* = RuntimeSupport.findSuperMethod(this, <overridden name>, <desc>);
* methods[index * 2 + 1]
* = RuntimeSupport.findMethod(this, <delegator name>, <desc>);
* or = null // the original method is abstract.
* }
+ * :
* return ($r)handler.invoke(this, methods[index * 2],
* methods[index * 2 + 1], $args);
*/
@@ -1143,7 +1421,7 @@ public class ProxyFactory {
code.addGetstatic(thisClassName, HOLDER, HOLDER_TYPE);
code.addAstore(arrayVar);
- callFind2Methods(code, meth.getName(), delegatorName, origIndex, desc, arrayVar);
+ forwarders.add(new Find2MethodsArgs(meth.getName(), delegatorName, desc, origIndex));
code.addAload(0);
code.addGetfield(thisClassName, HANDLER, HANDLER_TYPE);
@@ -1161,7 +1439,7 @@ public class ProxyFactory {
code.addInvokeinterface(MethodHandler.class.getName(), "invoke",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
5);
- Class retType = meth.getReturnType();
+ Class<?> retType = meth.getReturnType();
addUnwrapper(code, retType);
addReturn(code, retType);
@@ -1170,13 +1448,25 @@ public class ProxyFactory {
return forwarder;
}
+ static class Find2MethodsArgs {
+ String methodName, delegatorName, descriptor;
+ int origIndex;
+
+ Find2MethodsArgs(String mname, String dname, String desc, int index) {
+ methodName = mname;
+ delegatorName = dname;
+ descriptor = desc;
+ origIndex = index;
+ }
+ }
+
private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) {
- Class[] exceptions = orig.getExceptionTypes();
+ Class<?>[] exceptions = orig.getExceptionTypes();
setThrows(minfo, cp, exceptions);
}
private static void setThrows(MethodInfo minfo, ConstPool cp,
- Class[] exceptions) {
+ Class<?>[] exceptions) {
if (exceptions.length == 0)
return;
@@ -1189,7 +1479,7 @@ public class ProxyFactory {
minfo.setExceptionsAttribute(ea);
}
- private static int addLoadParameters(Bytecode code, Class[] params,
+ private static int addLoadParameters(Bytecode code, Class<?>[] params,
int offset) {
int stacksize = 0;
int n = params.length;
@@ -1199,7 +1489,7 @@ public class ProxyFactory {
return stacksize;
}
- private static int addLoad(Bytecode code, int n, Class type) {
+ private static int addLoad(Bytecode code, int n, Class<?> type) {
if (type.isPrimitive()) {
if (type == Long.TYPE) {
code.addLload(n);
@@ -1220,7 +1510,7 @@ public class ProxyFactory {
return 1;
}
- private static int addReturn(Bytecode code, Class type) {
+ private static int addReturn(Bytecode code, Class<?> type) {
if (type.isPrimitive()) {
if (type == Long.TYPE) {
code.addOpcode(Opcode.LRETURN);
@@ -1245,7 +1535,7 @@ public class ProxyFactory {
return 1;
}
- private static void makeParameterList(Bytecode code, Class[] params) {
+ private static void makeParameterList(Bytecode code, Class<?>[] params) {
int regno = 1;
int n = params.length;
code.addIconst(n);
@@ -1253,7 +1543,7 @@ public class ProxyFactory {
for (int i = 0; i < n; i++) {
code.addOpcode(Opcode.DUP);
code.addIconst(i);
- Class type = params[i];
+ Class<?> type = params[i];
if (type.isPrimitive())
regno = makeWrapper(code, type, regno);
else {
@@ -1265,7 +1555,7 @@ public class ProxyFactory {
}
}
- private static int makeWrapper(Bytecode code, Class type, int regno) {
+ private static int makeWrapper(Bytecode code, Class<?> type, int regno) {
int index = FactoryHelper.typeIndex(type);
String wrapper = FactoryHelper.wrapperTypes[index];
code.addNew(wrapper);
@@ -1276,29 +1566,7 @@ public class ProxyFactory {
return regno + FactoryHelper.dataSize[index];
}
- /**
- * @param thisMethod might be null.
- */
- private static void callFind2Methods(Bytecode code, String superMethod, String thisMethod,
- int index, String desc, int arrayVar) {
- String findClass = RuntimeSupport.class.getName();
- String findDesc
- = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/reflect/Method;)V";
-
- code.addAload(0);
- code.addLdc(superMethod);
- if (thisMethod == null)
- code.addOpcode(Opcode.ACONST_NULL);
- else
- code.addLdc(thisMethod);
-
- code.addIconst(index);
- code.addLdc(desc);
- code.addAload(arrayVar);
- code.addInvokestatic(findClass, "find2Methods", findDesc);
- }
-
- private static void addUnwrapper(Bytecode code, Class type) {
+ private static void addUnwrapper(Bytecode code, Class<?> type) {
if (type.isPrimitive()) {
if (type == Void.TYPE)
code.addOpcode(Opcode.POP);
diff --git a/src/main/javassist/util/proxy/ProxyObject.java b/src/main/javassist/util/proxy/ProxyObject.java
index 08febd6..a5a39e2 100644
--- a/src/main/javassist/util/proxy/ProxyObject.java
+++ b/src/main/javassist/util/proxy/ProxyObject.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -17,20 +18,28 @@ package javassist.util.proxy;
/**
* The interface implemented by proxy classes.
+ * This interface is available only if the super class of the proxy object
+ * does not have a <code>getHandler()</code> method. If the super class
+ * has <code>getHandler</code>, then <code>Proxy</code> interface is
+ * available.
*
* @see ProxyFactory
+ * @see Proxy
*/
-public interface ProxyObject {
+public interface ProxyObject extends Proxy {
/**
* Sets a handler. It can be used for changing handlers
* during runtime.
*/
+ @Override
void setHandler(MethodHandler mi);
/**
* Get the handler.
- * This can be used to access values of the underlying MethodHandler
- * or to serialize it properly.
+ * This can be used to access the underlying MethodHandler
+ * or to serialize it properly.
+ *
+ * @see ProxyFactory#getHandler(Proxy)
*/
MethodHandler getHandler();
}
diff --git a/src/main/javassist/util/proxy/ProxyObjectInputStream.java b/src/main/javassist/util/proxy/ProxyObjectInputStream.java
index 62c5eea..2992cc9 100644
--- a/src/main/javassist/util/proxy/ProxyObjectInputStream.java
+++ b/src/main/javassist/util/proxy/ProxyObjectInputStream.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -62,13 +63,14 @@ public class ProxyObjectInputStream extends ObjectInputStream
}
}
+ @Override
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
boolean isProxy = readBoolean();
if (isProxy) {
String name = (String)readObject();
- Class superClass = loader.loadClass(name);
+ Class<?> superClass = loader.loadClass(name);
int length = readInt();
- Class[] interfaces = new Class[length];
+ Class<?>[] interfaces = new Class[length];
for (int i = 0; i < length; i++) {
name = (String)readObject();
interfaces[i] = loader.loadClass(name);
@@ -83,11 +85,10 @@ public class ProxyObjectInputStream extends ObjectInputStream
factory.setUseWriteReplace(false);
factory.setSuperclass(superClass);
factory.setInterfaces(interfaces);
- Class proxyClass = factory.createClass(signature);
+ Class<?> proxyClass = factory.createClass(signature);
return ObjectStreamClass.lookup(proxyClass);
- } else {
- return super.readClassDescriptor();
}
+ return super.readClassDescriptor();
}
/**
@@ -96,4 +97,4 @@ public class ProxyObjectInputStream extends ObjectInputStream
* the input stream or the system class loader if the context class loader is null.
*/
private ClassLoader loader;
-} \ No newline at end of file
+}
diff --git a/src/main/javassist/util/proxy/ProxyObjectOutputStream.java b/src/main/javassist/util/proxy/ProxyObjectOutputStream.java
index b2f8bd4..8735168 100644
--- a/src/main/javassist/util/proxy/ProxyObjectOutputStream.java
+++ b/src/main/javassist/util/proxy/ProxyObjectOutputStream.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -43,20 +44,21 @@ public class ProxyObjectOutputStream extends ObjectOutputStream
super(out);
}
+ @Override
protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException {
- Class cl = desc.forClass();
+ Class<?> cl = desc.forClass();
if (ProxyFactory.isProxyClass(cl)) {
writeBoolean(true);
- Class superClass = cl.getSuperclass();
- Class[] interfaces = cl.getInterfaces();
+ Class<?> superClass = cl.getSuperclass();
+ Class<?>[] interfaces = cl.getInterfaces();
byte[] signature = ProxyFactory.getFilterSignature(cl);
String name = superClass.getName();
writeObject(name);
// we don't write the marker interface ProxyObject
writeInt(interfaces.length - 1);
for (int i = 0; i < interfaces.length; i++) {
- Class interfaze = interfaces[i];
- if (interfaze != ProxyObject.class) {
+ Class<?> interfaze = interfaces[i];
+ if (interfaze != ProxyObject.class && interfaze != Proxy.class) {
name = interfaces[i].getName();
writeObject(name);
}
diff --git a/src/main/javassist/util/proxy/RuntimeSupport.java b/src/main/javassist/util/proxy/RuntimeSupport.java
index 817ab4c..0bd0cce 100644
--- a/src/main/javassist/util/proxy/RuntimeSupport.java
+++ b/src/main/javassist/util/proxy/RuntimeSupport.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,8 +16,8 @@
package javassist.util.proxy;
-import java.lang.reflect.Method;
import java.io.Serializable;
+import java.lang.reflect.Method;
/**
* Runtime support routines that the classes generated by ProxyFactory use.
@@ -30,6 +31,10 @@ public class RuntimeSupport {
public static MethodHandler default_interceptor = new DefaultMethodHandler();
static class DefaultMethodHandler implements MethodHandler, Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ @Override
public Object invoke(Object self, Method m,
Method proceed, Object[] args)
throws Exception
@@ -45,29 +50,63 @@ public class RuntimeSupport {
* @throws RuntimeException if the methods are not found.
* @see javassist.util.proxy.ProxyFactory
*/
+ public static void find2Methods(Class<?> clazz, String superMethod,
+ String thisMethod, int index,
+ String desc, java.lang.reflect.Method[] methods)
+ {
+ methods[index + 1] = thisMethod == null ? null
+ : findMethod(clazz, thisMethod, desc);
+ methods[index] = findSuperClassMethod(clazz, superMethod, desc);
+ }
+
+ /**
+ * Finds two methods specified by the parameters and stores them
+ * into the given array.
+ *
+ * <p>Added back for JBoss Seam. See JASSIST-206.</p>
+ *
+ * @throws RuntimeException if the methods are not found.
+ * @see javassist.util.proxy.ProxyFactory
+ * @deprecated replaced by {@link #find2Methods(Class, String, String, int, String, Method[])}
+ */
+ @Deprecated
public static void find2Methods(Object self, String superMethod,
String thisMethod, int index,
String desc, java.lang.reflect.Method[] methods)
{
- synchronized (methods) {
- if (methods[index] == null) {
- methods[index + 1] = thisMethod == null ? null
- : findMethod(self, thisMethod, desc);
- methods[index] = findSuperMethod(self, superMethod, desc);
- }
- }
+ methods[index + 1] = thisMethod == null ? null
+ : findMethod(self, thisMethod, desc);
+ methods[index] = findSuperMethod(self, superMethod, desc);
}
/**
* Finds a method with the given name and descriptor.
* It searches only the class of self.
*
+ * <p>Added back for JBoss Seam. See JASSIST-206.</p>
+ *
* @throws RuntimeException if the method is not found.
+ * @deprecated replaced by {@link #findMethod(Class, String, String)}
*/
+ @Deprecated
public static Method findMethod(Object self, String name, String desc) {
Method m = findMethod2(self.getClass(), name, desc);
if (m == null)
- error(self, name, desc);
+ error(self.getClass(), name, desc);
+
+ return m;
+ }
+
+ /**
+ * Finds a method with the given name and descriptor.
+ * It searches only the class of self.
+ *
+ * @throws RuntimeException if the method is not found.
+ */
+ public static Method findMethod(Class<?> clazz, String name, String desc) {
+ Method m = findMethod2(clazz, name, desc);
+ if (m == null)
+ error(clazz, name, desc);
return m;
}
@@ -79,28 +118,39 @@ public class RuntimeSupport {
* @throws RuntimeException if the method is not found.
*/
public static Method findSuperMethod(Object self, String name, String desc) {
- Class clazz = self.getClass();
+ // for JBoss Seam. See JASSIST-183.
+ Class<?> clazz = self.getClass();
+ return findSuperClassMethod(clazz, name, desc);
+ }
+
+ /**
+ * Finds a method that has the given name and descriptor and is declared
+ * in the super class.
+ *
+ * @throws RuntimeException if the method is not found.
+ */
+ public static Method findSuperClassMethod(Class<?> clazz, String name, String desc) {
Method m = findSuperMethod2(clazz.getSuperclass(), name, desc);
if (m == null)
m = searchInterfaces(clazz, name, desc);
if (m == null)
- error(self, name, desc);
+ error(clazz, name, desc);
return m;
}
- private static void error(Object self, String name, String desc) {
+ private static void error(Class<?> clazz, String name, String desc) {
throw new RuntimeException("not found " + name + ":" + desc
- + " in " + self.getClass().getName());
+ + " in " + clazz.getName());
}
- private static Method findSuperMethod2(Class clazz, String name, String desc) {
+ private static Method findSuperMethod2(Class<?> clazz, String name, String desc) {
Method m = findMethod2(clazz, name, desc);
if (m != null)
return m;
- Class superClass = clazz.getSuperclass();
+ Class<?> superClass = clazz.getSuperclass();
if (superClass != null) {
m = findSuperMethod2(superClass, name, desc);
if (m != null)
@@ -110,9 +160,9 @@ public class RuntimeSupport {
return searchInterfaces(clazz, name, desc);
}
- private static Method searchInterfaces(Class clazz, String name, String desc) {
+ private static Method searchInterfaces(Class<?> clazz, String name, String desc) {
Method m = null;
- Class[] interfaces = clazz.getInterfaces();
+ Class<?>[] interfaces = clazz.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
m = findSuperMethod2(interfaces[i], name, desc);
if (m != null)
@@ -122,7 +172,7 @@ public class RuntimeSupport {
return m;
}
- private static Method findMethod2(Class clazz, String name, String desc) {
+ private static Method findMethod2(Class<?> clazz, String name, String desc) {
Method[] methods = SecurityActions.getDeclaredMethods(clazz);
int n = methods.length;
for (int i = 0; i < n; i++)
@@ -137,7 +187,7 @@ public class RuntimeSupport {
* Makes a descriptor for a given method.
*/
public static String makeDescriptor(Method m) {
- Class[] params = m.getParameterTypes();
+ Class<?>[] params = m.getParameterTypes();
return makeDescriptor(params, m.getReturnType());
}
@@ -147,18 +197,32 @@ public class RuntimeSupport {
* @param params parameter types.
* @param retType return type.
*/
- public static String makeDescriptor(Class[] params, Class retType) {
+ public static String makeDescriptor(Class<?>[] params, Class<?> retType) {
StringBuffer sbuf = new StringBuffer();
sbuf.append('(');
for (int i = 0; i < params.length; i++)
makeDesc(sbuf, params[i]);
sbuf.append(')');
+ if (retType != null)
+ makeDesc(sbuf, retType);
+
+ return sbuf.toString();
+ }
+
+ /**
+ * Makes a descriptor for a given method.
+ *
+ * @param params the descriptor of parameter types.
+ * @param retType return type.
+ */
+ public static String makeDescriptor(String params, Class<?> retType) {
+ StringBuffer sbuf = new StringBuffer(params);
makeDesc(sbuf, retType);
return sbuf.toString();
}
- private static void makeDesc(StringBuffer sbuf, Class type) {
+ private static void makeDesc(StringBuffer sbuf, Class<?> type) {
if (type.isArray()) {
sbuf.append('[');
makeDesc(sbuf, type.getComponentType());
@@ -200,11 +264,13 @@ public class RuntimeSupport {
public static SerializedProxy makeSerializedProxy(Object proxy)
throws java.io.InvalidClassException
{
- Class clazz = proxy.getClass();
+ Class<?> clazz = proxy.getClass();
MethodHandler methodHandler = null;
if (proxy instanceof ProxyObject)
methodHandler = ((ProxyObject)proxy).getHandler();
+ else if (proxy instanceof Proxy)
+ methodHandler = ProxyFactory.getHandler((Proxy)proxy);
return new SerializedProxy(clazz, ProxyFactory.getFilterSignature(clazz), methodHandler);
}
diff --git a/src/main/javassist/util/proxy/SecurityActions.java b/src/main/javassist/util/proxy/SecurityActions.java
index 40741e4..c940561 100644..100755
--- a/src/main/javassist/util/proxy/SecurityActions.java
+++ b/src/main/javassist/util/proxy/SecurityActions.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -14,6 +15,8 @@
*/
package javassist.util.proxy;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -22,46 +25,99 @@ import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
-class SecurityActions {
- static Method[] getDeclaredMethods(final Class clazz) {
+import javassist.bytecode.ClassFile;
+
+class SecurityActions extends SecurityManager
+{
+ public static final SecurityActions stack = new SecurityActions();
+
+ /**
+ * Since Java 9 abruptly removed <code>Reflection.getCallerClass()</code>
+ * in favour of <code>StackWalker</code> we are left having to find a
+ * solution for the older versions without upsetting the new compiler.
+ *
+ * The member scoped function <code>getClassContext()</code>
+ * available as a <code>SecurityManager</code> sibling remains
+ * functional across all versions, for now.
+ *
+ * @return represents the declaring class of the method that invoked
+ * the method that called this or index 2 on the stack trace.
+ * @since 3.23
+ */
+ public Class<?> getCallerClass() {
+ return getClassContext()[2];
+ }
+
+ static Method[] getDeclaredMethods(final Class<?> clazz)
+ {
if (System.getSecurityManager() == null)
return clazz.getDeclaredMethods();
else {
- return (Method[]) AccessController
- .doPrivileged(new PrivilegedAction() {
- public Object run() {
- return clazz.getDeclaredMethods();
- }
- });
+ return AccessController.doPrivileged(
+ new PrivilegedAction<Method[]>() {
+ public Method[] run() {
+ return clazz.getDeclaredMethods();
+ }
+ });
}
}
- static Constructor[] getDeclaredConstructors(final Class clazz) {
+ static Constructor<?>[] getDeclaredConstructors(final Class<?> clazz)
+ {
if (System.getSecurityManager() == null)
return clazz.getDeclaredConstructors();
else {
- return (Constructor[]) AccessController
- .doPrivileged(new PrivilegedAction() {
- public Object run() {
- return clazz.getDeclaredConstructors();
- }
- });
+ return AccessController.doPrivileged(
+ new PrivilegedAction<Constructor<?>[]>() {
+ public Constructor<?>[] run() {
+ return clazz.getDeclaredConstructors();
+ }
+ });
}
}
- static Method getDeclaredMethod(final Class clazz, final String name,
- final Class[] types) throws NoSuchMethodException {
+ static MethodHandle getMethodHandle(final Class<?> clazz, final
+ String name, final Class<?>[] params) throws NoSuchMethodException
+ {
+ try {
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<MethodHandle>() {
+ public MethodHandle run() throws IllegalAccessException,
+ NoSuchMethodException, SecurityException {
+ Method rmet = clazz.getDeclaredMethod(name, params);
+ rmet.setAccessible(true);
+ MethodHandle meth = MethodHandles.lookup().unreflect(rmet);
+ rmet.setAccessible(false);
+ return meth;
+ }
+ });
+ }
+ catch (PrivilegedActionException e) {
+ if (e.getCause() instanceof NoSuchMethodException)
+ throw (NoSuchMethodException) e.getCause();
+ throw new RuntimeException(e.getCause());
+ }
+ }
+
+ static Method getDeclaredMethod(final Class<?> clazz, final String name,
+ final Class<?>[] types) throws NoSuchMethodException
+ {
if (System.getSecurityManager() == null)
return clazz.getDeclaredMethod(name, types);
else {
try {
- return (Method) AccessController
- .doPrivileged(new PrivilegedExceptionAction() {
- public Object run() throws Exception {
- return clazz.getDeclaredMethod(name, types);
- }
- });
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Method>() {
+ public Method run() throws Exception {
+ return clazz.getDeclaredMethod(name, types);
+ }
+ });
}
catch (PrivilegedActionException e) {
if (e.getCause() instanceof NoSuchMethodException)
@@ -72,20 +128,20 @@ class SecurityActions {
}
}
- static Constructor getDeclaredConstructor(final Class clazz,
- final Class[] types)
+ static Constructor<?> getDeclaredConstructor(final Class<?> clazz,
+ final Class<?>[] types)
throws NoSuchMethodException
{
if (System.getSecurityManager() == null)
return clazz.getDeclaredConstructor(types);
else {
try {
- return (Constructor) AccessController
- .doPrivileged(new PrivilegedExceptionAction() {
- public Object run() throws Exception {
- return clazz.getDeclaredConstructor(types);
- }
- });
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Constructor<?>>() {
+ public Constructor<?> run() throws Exception {
+ return clazz.getDeclaredConstructor(types);
+ }
+ });
}
catch (PrivilegedActionException e) {
if (e.getCause() instanceof NoSuchMethodException)
@@ -97,12 +153,13 @@ class SecurityActions {
}
static void setAccessible(final AccessibleObject ao,
- final boolean accessible) {
+ final boolean accessible)
+ {
if (System.getSecurityManager() == null)
ao.setAccessible(accessible);
else {
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
ao.setAccessible(accessible);
return null;
}
@@ -117,19 +174,113 @@ class SecurityActions {
fld.set(target, value);
else {
try {
- AccessController.doPrivileged(new PrivilegedExceptionAction() {
- public Object run() throws Exception {
- fld.set(target, value);
- return null;
- }
- });
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Void>() {
+ public Void run() throws Exception {
+ fld.set(target, value);
+ return null;
+ }
+ });
}
catch (PrivilegedActionException e) {
if (e.getCause() instanceof NoSuchMethodException)
throw (IllegalAccessException) e.getCause();
-
throw new RuntimeException(e.getCause());
}
}
}
+
+ static TheUnsafe getSunMiscUnsafeAnonymously() throws ClassNotFoundException
+ {
+ try {
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<TheUnsafe>() { public TheUnsafe run() throws
+ ClassNotFoundException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException {
+ Class<?> unsafe = Class.forName("sun.misc.Unsafe");
+ Field theUnsafe = unsafe.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ TheUnsafe usf = stack.new TheUnsafe(unsafe, theUnsafe.get(null));
+ theUnsafe.setAccessible(false);
+ disableWarning(usf);
+ return usf;
+ }
+ });
+ }
+ catch (PrivilegedActionException e) {
+ if (e.getCause() instanceof ClassNotFoundException)
+ throw (ClassNotFoundException) e.getCause();
+ if (e.getCause() instanceof NoSuchFieldException)
+ throw new ClassNotFoundException("No such instance.", e.getCause());
+ if (e.getCause() instanceof IllegalAccessException
+ || e.getCause() instanceof IllegalAccessException
+ || e.getCause() instanceof SecurityException)
+ throw new ClassNotFoundException("Security denied access.", e.getCause());
+ throw new RuntimeException(e.getCause());
+ }
+ }
+ /**
+ * _The_ Notorious sun.misc.Unsafe in all its glory, but anonymous
+ * so as not to attract unwanted attention. Kept in two separate
+ * parts it manages to avoid detection from linker/compiler/general
+ * complainers and those. This functionality will vanish from the
+ * JDK soon but in the meantime it shouldn't be an obstacle.
+ *
+ * All exposed methods are cached in a dictionary with overloaded
+ * methods collected under their corresponding keys. Currently the
+ * implementation assumes there is only one, if you need find a
+ * need there will have to be a compare.
+ * @since 3.23 */
+ class TheUnsafe
+ {
+ final Class<?> unsafe;
+ final Object theUnsafe;
+ final Map<String, List<Method>> methods =
+ new HashMap<String, List<Method>>();
+
+ TheUnsafe(Class<?> c, Object o)
+ {
+ this.unsafe = c;
+ this.theUnsafe = o;
+ for (Method m: unsafe.getDeclaredMethods()) {
+ if (!methods.containsKey(m.getName())) {
+ methods.put(m.getName(), Collections.singletonList(m));
+ continue;
+ }
+ if (methods.get(m.getName()).size() == 1)
+ methods.put(m.getName(),
+ new ArrayList<Method>(methods.get(m.getName())));
+ methods.get(m.getName()).add(m);
+ }
+ }
+
+ private Method getM(String name, Object[] o)
+ {
+ return methods.get(name).get(0);
+ }
+
+ public Object call(String name, Object... args)
+ {
+ try {
+ return getM(name, args).invoke(theUnsafe, args);
+ } catch (Throwable t) {t.printStackTrace();}
+ return null;
+ }
+ }
+ /**
+ * Java 9 now complains about every privileged action regardless.
+ * Displaying warnings of "illegal usage" and then instructing users
+ * to go hassle the maintainers in order to have it fixed.
+ * Making it hush for now, see all fixed.
+ * @param tu theUnsafe that'll fix it */
+ static void disableWarning(TheUnsafe tu) {
+ try {
+ if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_9)
+ return;
+ Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
+ Field logger = cls.getDeclaredField("logger");
+ tu.call("putObjectVolatile", cls, tu.call("staticFieldOffset", logger), null);
+ } catch (Exception e) { /*swallow*/ }
+ }
}
+
diff --git a/src/main/javassist/util/proxy/SerializedProxy.java b/src/main/javassist/util/proxy/SerializedProxy.java
index cddfab4..1417a04 100644
--- a/src/main/javassist/util/proxy/SerializedProxy.java
+++ b/src/main/javassist/util/proxy/SerializedProxy.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
@@ -15,12 +16,14 @@
package javassist.util.proxy;
-import java.io.Serializable;
+import java.io.InvalidClassException;
+import java.io.InvalidObjectException;
import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
-import java.security.ProtectionDomain;
/**
* A proxy object is converted into an instance of this class
@@ -29,22 +32,25 @@ import java.security.ProtectionDomain;
* @see RuntimeSupport#makeSerializedProxy(Object)
*/
class SerializedProxy implements Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
private String superClass;
private String[] interfaces;
private byte[] filterSignature;
private MethodHandler handler;
- SerializedProxy(Class proxy, byte[] sig, MethodHandler h) {
+ SerializedProxy(Class<?> proxy, byte[] sig, MethodHandler h) {
filterSignature = sig;
handler = h;
superClass = proxy.getSuperclass().getName();
- Class[] infs = proxy.getInterfaces();
+ Class<?>[] infs = proxy.getInterfaces();
int n = infs.length;
interfaces = new String[n - 1];
String setterInf = ProxyObject.class.getName();
+ String setterInf2 = Proxy.class.getName();
for (int i = 0; i < n; i++) {
String name = infs[i].getName();
- if (!name.equals(setterInf))
+ if (!name.equals(setterInf) && !name.equals(setterInf2))
interfaces[i] = name;
}
}
@@ -56,10 +62,11 @@ class SerializedProxy implements Serializable {
* @return loaded class
* @throws ClassNotFoundException for any error
*/
- protected Class loadClass(final String className) throws ClassNotFoundException {
+ protected Class<?> loadClass(final String className) throws ClassNotFoundException {
try {
- return (Class)AccessController.doPrivileged(new PrivilegedExceptionAction(){
- public Object run() throws Exception{
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>(){
+ @Override
+ public Class<?> run() throws Exception{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return Class.forName(className, true, cl);
}
@@ -73,25 +80,31 @@ class SerializedProxy implements Serializable {
Object readResolve() throws ObjectStreamException {
try {
int n = interfaces.length;
- Class[] infs = new Class[n];
+ Class<?>[] infs = new Class[n];
for (int i = 0; i < n; i++)
infs[i] = loadClass(interfaces[i]);
ProxyFactory f = new ProxyFactory();
f.setSuperclass(loadClass(superClass));
f.setInterfaces(infs);
- ProxyObject proxy = (ProxyObject)f.createClass(filterSignature).newInstance();
+ Proxy proxy = (Proxy)f.createClass(filterSignature).getConstructor().newInstance();
proxy.setHandler(handler);
return proxy;
}
+ catch (NoSuchMethodException e) {
+ throw new InvalidClassException(e.getMessage());
+ }
+ catch (InvocationTargetException e) {
+ throw new InvalidClassException(e.getMessage());
+ }
catch (ClassNotFoundException e) {
- throw new java.io.InvalidClassException(e.getMessage());
+ throw new InvalidClassException(e.getMessage());
}
catch (InstantiationException e2) {
- throw new java.io.InvalidObjectException(e2.getMessage());
+ throw new InvalidObjectException(e2.getMessage());
}
catch (IllegalAccessException e3) {
- throw new java.io.InvalidClassException(e3.getMessage());
+ throw new InvalidClassException(e3.getMessage());
}
}
}
diff --git a/src/test/DefineClassCapability.java b/src/test/DefineClassCapability.java
new file mode 100644
index 0000000..c6dec20
--- /dev/null
+++ b/src/test/DefineClassCapability.java
@@ -0,0 +1,5 @@
+/*
+ * This is used as a capability for running CtClass#toClass().
+ */
+public class DefineClassCapability {
+}
diff --git a/src/test/Jassist150.java b/src/test/Jassist150.java
new file mode 100644
index 0000000..2d57da0
--- /dev/null
+++ b/src/test/Jassist150.java
@@ -0,0 +1,111 @@
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.expr.ExprEditor;
+import javassist.expr.MethodCall;
+
+@SuppressWarnings("unused")
+public class Jassist150 {
+ public static final String BASE_PATH = "./";
+ public static final String JAVASSIST_JAR = BASE_PATH + "javassist.jar";
+ public static final String CLASSES_FOLDER = BASE_PATH + "build/classes";
+ public static final String TEST_CLASSES_FOLDER = BASE_PATH
+ + "build/test-classes";
+
+ public static class Inner1 {
+ public static int get() {
+ return 0;
+ }
+ }
+
+ public static void implTestClassTailCache() throws NotFoundException,
+ CannotCompileException {
+ ClassPool pool = new ClassPool(true);
+ for (int paths = 0; paths < 50; paths++) {
+ pool.appendClassPath(JAVASSIST_JAR);
+ pool.appendClassPath(CLASSES_FOLDER);
+ pool.appendClassPath(TEST_CLASSES_FOLDER);
+ }
+ CtClass cc = pool.get("Jassist150$Inner1");
+ CtMethod ccGet = cc.getDeclaredMethod("get");
+ String code1 = "{ int n1 = Integer.valueOf(1); "
+ + " int n2 = Integer.valueOf(2); "
+ + " int n3 = Integer.valueOf(3); "
+ + " int n4 = Integer.valueOf(4); "
+ + " int n5 = Integer.valueOf(5); "
+ + " return n1+n2+n3+n4+n5; }";
+ String code2 = "{ int n1 = java.lang.Integer.valueOf(1); "
+ + " int n2 = java.lang.Integer.valueOf(2); "
+ + " int n3 = java.lang.Integer.valueOf(3); "
+ + " int n4 = java.lang.Integer.valueOf(4); "
+ + " int n5 = java.lang.Integer.valueOf(5); "
+ + " return n1+n2+n3+n4+n5; }";
+ String code3 = "{ int n1 = java.lang.Integer#valueOf(1); "
+ + " int n2 = java.lang.Integer#valueOf(2); "
+ + " int n3 = java.lang.Integer#valueOf(3); "
+ + " int n4 = java.lang.Integer#valueOf(4); "
+ + " int n5 = java.lang.Integer#valueOf(5); "
+ + " return n1+n2+n3+n4+n5; }";
+ loop(cc, ccGet, code1);
+ }
+
+ public static void loop(CtClass cc, CtMethod ccGet, String code)
+ throws CannotCompileException {
+ long startTime = System.currentTimeMillis();
+ for (int replace = 0; replace < 1000; replace++) {
+ ccGet.setBody(code);
+ }
+ long endTime = System.currentTimeMillis();
+ System.out.println("Test: Time (ms) " + (endTime - startTime));
+ }
+
+ public static void implTestClassTailCache2() throws NotFoundException,
+ CannotCompileException {
+ ClassPool pool = new ClassPool(true);
+ for (int paths = 0; paths < 50; paths++) {
+ pool.appendClassPath(JAVASSIST_JAR);
+ pool.appendClassPath(CLASSES_FOLDER);
+ pool.appendClassPath(TEST_CLASSES_FOLDER);
+ }
+ CtClass cc = pool.get("Jassist150$Inner1");
+ CtMethod ccGet = cc.getDeclaredMethod("get");
+ String code3 = "{ int n1 = java.lang.Integer#valueOf(1); "
+ + " int n2 = java.lang.Integer#valueOf(2); "
+ + " int n3 = java.lang.Integer#valueOf(3); "
+ + " int n4 = java.lang.Integer#valueOf(4); "
+ + " int n5 = java.lang.Integer#valueOf(5); "
+ + " return n1+n2+n3+n4+n5; }";
+ ccGet.setBody(code3);
+ }
+
+ public void testJIRA152() throws Exception {
+ CtClass cc = ClassPool.getDefault().get("test4.JIRA152");
+ CtMethod mth = cc.getDeclaredMethod("buildColumnOverride");
+ mth.instrument(new ExprEditor() {
+ public void edit(MethodCall c) throws CannotCompileException {
+ c.replace("try{ $_ = $proceed($$); } catch (Throwable t) { throw t; }");
+ }
+ });
+ mth.getMethodInfo().rebuildStackMap(ClassPool.getDefault());
+ cc.writeFile();
+ }
+
+ public static void main(String[] args) throws Exception {
+ new Jassist150().testJIRA152();
+ }
+
+ public static void main2(String[] args) {
+ for (int loop = 0; loop < 5; loop++) {
+ try {
+ implTestClassTailCache();
+ for (int i = 0; i < 100; i++)
+ implTestClassTailCache2();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ System.out.println("size: " + javassist.compiler.MemberResolver.getInvalidMapSize());
+ }
+}
diff --git a/src/test/Readme.txt b/src/test/Readme.txt
new file mode 100644
index 0000000..3218a9d
--- /dev/null
+++ b/src/test/Readme.txt
@@ -0,0 +1,18 @@
+# How to run tests
+
+Requirements:
+Java JDK 9
+Maven
+
+1) Build jar file and move it to the top level folder.
+
+ > mvn package
+ > mv ./target/javassist*-GA.jar ./javaassist.jar
+
+2) Check that ./src/test/javassist/JvstTestRoot.PATH and .JAR_PATH point to the compiled jar file.
+ The default is "../../".
+
+3) Run Tests
+
+ > mvn test
+ > mvn surefire:test
diff --git a/src/test/Test.java b/src/test/Test.java
new file mode 100644
index 0000000..9907406
--- /dev/null
+++ b/src/test/Test.java
@@ -0,0 +1,55 @@
+import java.util.ArrayList;
+import java.util.List;
+import javassist.*;
+
+@SuppressWarnings("unused")
+class InvalidStackMapFrame {
+
+ public void bytecodeVerifyError1() {
+ String[] newLine = new String[10];
+ for (int i = 0; i < 5; i++) {
+ String a = newLine[1];
+ newLine[4] = a;
+ }
+ }
+
+ public void bytecodeVerifyError() {
+ // javassist bug : invalid stack map frame
+ List<Integer> test = new ArrayList<Integer>();
+ String[] newLine = new String[10];
+ for (Integer idx : test) {
+ // invalid stackMapFrame
+ // FRAME FULL [bug_regression_jdk7/javassist/InvalidStackMapFrame java/util/ArrayList java/lang/Object java/util/Iterator T T T I] []
+ // java/lang/Object is wrong -> [Ljava/lang/String; is correct
+ String address = newLine[1];
+ int tabPos = -1;
+ if (tabPos != -1) {
+ address = address.substring(tabPos + 1);
+ }
+ newLine[4] = address;
+ }
+
+ }
+}
+
+public class Test {
+ private static final String INVALID_STACK_MAP_FRAME = "InvalidStackMapFrame";
+
+ public static void main(String[] args) throws Exception {
+
+ // CustomURLClassLoader classLoader = new CustomURLClassLoader(new URL[]{}, Thread.currentThread().getContextClassLoader());
+
+ ClassPool classPool = ClassPool.getDefault();
+ // classPool.appendClassPath(new LoaderClassPath(classLoader));
+
+ final CtClass ctClass = classPool.get(INVALID_STACK_MAP_FRAME);
+ final CtMethod method = ctClass.getDeclaredMethod("bytecodeVerifyError");
+ method.addLocalVariable("test_localVariable", CtClass.intType);
+ method.insertBefore("{ test_localVariable = 1; }");
+ ctClass.debugWriteFile();
+ Class<?> cc = ctClass.toClass();
+ System.out.println(cc.getName());
+ InvalidStackMapFrame obj = (InvalidStackMapFrame)cc.getDeclaredConstructor().newInstance();
+ obj.bytecodeVerifyError();
+ }
+}
diff --git a/src/test/VisibleTop.java b/src/test/VisibleTop.java
new file mode 100644
index 0000000..0bb6161
--- /dev/null
+++ b/src/test/VisibleTop.java
@@ -0,0 +1,7 @@
+@SuppressWarnings("unused")
+public class VisibleTop {
+ public int pub;
+ protected int pro;
+ private int pri;
+ int pack;
+}
diff --git a/src/test/VisibleTop2.java b/src/test/VisibleTop2.java
new file mode 100644
index 0000000..a09a139
--- /dev/null
+++ b/src/test/VisibleTop2.java
@@ -0,0 +1,7 @@
+@SuppressWarnings("unused")
+public class VisibleTop2 {
+ public int pub;
+ protected int pro;
+ private int pri;
+ int pack;
+}
diff --git a/src/test/annotation/Test.java b/src/test/annotation/Test.java
new file mode 100644
index 0000000..2c9da5e
--- /dev/null
+++ b/src/test/annotation/Test.java
@@ -0,0 +1,35 @@
+package annotation;
+
+@interface Id {
+ int id();
+}
+
+enum EnumTest {
+ A, B, C
+}
+
+@interface Tag {
+ boolean z();
+ byte b();
+ char c();
+ short s();
+ int i();
+ long j();
+ float f();
+ double d();
+ String string();
+ Class<? extends Object> integer();
+ EnumTest enumtest();
+ String[] array();
+ Id annotation();
+}
+
+@Tag(z = true, b = 1, c = 'a', s = 2, i = 3, j = 4L, f = 5.0F, d = 5.0,
+ string = "abc",
+ enumtest = EnumTest.A,
+ integer = Integer.class,
+ array = { "p", "q", "r" },
+ annotation = @Id(id = 20))
+public class Test {
+ public int test() { return 0; }
+}
diff --git a/src/test/javassist/Bench.java b/src/test/javassist/Bench.java
new file mode 100644
index 0000000..b7b8b27
--- /dev/null
+++ b/src/test/javassist/Bench.java
@@ -0,0 +1,162 @@
+package javassist;
+
+import junit.framework.*;
+import javassist.expr.*;
+import javassist.compiler.*;
+
+public class Bench extends JvstTestRoot {
+ public Bench(String name) {
+ super(name);
+ }
+
+ public void testProceed() throws Exception {
+ CtClass cc = sloader.get("test.BenchProceed");
+ CtMethod m1 = cc.getDeclaredMethod("p");
+ m1.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ if (m.getMethodName().equals("calc"))
+ m.replace("{ before($args); $_ = $proceed($$); }");
+ }
+ });
+
+ CtMethod m2 = cc.getDeclaredMethod("q");
+ m2.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ if (m.getMethodName().equals("calc"))
+ m.replace("{ $_ = ($r)replace($args); }");
+ }
+ });
+
+ CtMethod m3 = cc.getDeclaredMethod("s");
+ m3.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ if (m.getMethodName().equals("calc2"))
+ m.replace(
+ "{ long start = System.currentTimeMillis();"
+ + "$_ = $proceed($$);"
+ + "long elapsed = System.currentTimeMillis() - start;"
+ + "System.out.println(elapsed); }");
+ }
+ });
+
+ CtMethod m4 = cc.getDeclaredMethod("t");
+ m4.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ if (m.getMethodName().equals("calc2"))
+ m.replace(
+ "{ long start = System.currentTimeMillis();"
+ + "$_ = $proceed($$);"
+ + "System.out.println(System.currentTimeMillis() - start);"
+ + "}");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ int ptime = invoke(obj, "p");
+ int qtime = invoke(obj, "q");
+ System.out.println("time: (p) " + ptime + ", (q) " + qtime);
+ System.out.println("s:");
+ invoke(obj, "s");
+ System.out.println("t:");
+ invoke(obj, "t");
+ assertTrue(ptime < qtime);
+ }
+
+ public void testProceedNew() throws Exception {
+ CtClass cc = sloader.get("test.BenchProceedNew");
+ CtMethod m1 = cc.getDeclaredMethod("jvst0");
+ m1.instrument(new ExprEditor() {
+ public void edit(NewExpr m) throws CannotCompileException {
+ m.replace("{ $_ = $proceed($$); }");
+ }
+ });
+
+ CtMethod m2 = cc.getDeclaredMethod("jvst2");
+ m2.instrument(new ExprEditor() {
+ public void edit(NewExpr m) throws CannotCompileException {
+ m.replace("{ $_ = $proceed($$); }");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ int qtime = invoke(obj, "jvst0");
+ int ptime = invoke(obj, "org0");
+ System.out.println("time: (org0) " + ptime + ", (jvst0) " + qtime);
+ qtime = invoke(obj, "jvst2");
+ ptime = invoke(obj, "org2");
+ System.out.println("time: (org2) " + ptime + ", (jvst2) " + qtime);
+ }
+
+ public void testStaticMethod() throws Exception {
+ CtClass cc = sloader.get("test.BenchStaticMethod");
+ CtMethod m1 = cc.getDeclaredMethod("test");
+ m1.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ if (m.getMethodName().equals("foo"))
+ m.replace("{ num += $1; $_ = $proceed($$); }");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ int qtime = invoke(obj, "test");
+ int ptime = invoke(obj, "orgTest");
+ System.out.println(
+ "BenchStaticMethod time: (org) " + ptime + ", (jvst) " + qtime);
+ }
+
+ public void testStaticField() throws Exception {
+ System.out.println(sloader);
+ Javac jc = new Javac(sloader.get("test.StaticField"));
+ long t0 = System.currentTimeMillis();
+ for (int i = 0; i < 100; i++)
+ jc.compileStmnt("{ int counter = 0; counter++; }");
+
+ t0 = System.currentTimeMillis() - t0;
+ System.out.println("local variable: " + (t0 * 10) + " usec");
+
+ long t = System.currentTimeMillis();
+ for (int i = 0; i < 100; i++)
+ jc.compileStmnt("{ test.StaticField.counter++; }");
+
+ t = System.currentTimeMillis() - t;
+ System.out.println("StaticField: " + (t * 10) + " usec");
+
+ long t2 = System.currentTimeMillis();
+ for (int i = 0; i < 100; i++)
+ jc.compileStmnt("{ test.StaticField#counter++; }");
+
+ t2 = System.currentTimeMillis() - t2;
+ System.out.println("StaticField with #: " + (t2 * 10) + " usec");
+
+ long t3 = System.currentTimeMillis();
+ for (int i = 0; i < 100; i++)
+ jc.compileStmnt("{ StaticField.counter2++; }");
+
+ t3 = System.currentTimeMillis() - t3;
+ System.out.println("StaticField without package: " + (t3 * 10) + " usec");
+
+ long t4 = System.currentTimeMillis();
+ for (int i = 0; i < 100; i++)
+ jc.compileStmnt("{ test.StaticField.counter++; }");
+
+ t4 = System.currentTimeMillis() - t4;
+ System.out.println("StaticField: " + (t4 * 10) + " usec");
+
+ long t5 = System.currentTimeMillis();
+ for (int i = 0; i < 100; i++)
+ jc.compileStmnt("{ System.out.println(); }");
+
+ t5 = System.currentTimeMillis() - t5;
+ System.out.println("println: " + (t5 * 10) + " usec");
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Benchmark Tests");
+ suite.addTestSuite(Bench.class);
+ suite.addTestSuite(testproxy.ProxyFactoryPerformanceTest.class);
+ return suite;
+ }
+}
diff --git a/src/test/javassist/ClassPoolBench.java b/src/test/javassist/ClassPoolBench.java
new file mode 100644
index 0000000..bf71051
--- /dev/null
+++ b/src/test/javassist/ClassPoolBench.java
@@ -0,0 +1,58 @@
+package javassist;
+
+import java.util.Enumeration;
+import java.util.zip.*;
+
+public class ClassPoolBench {
+ static ClassPool cp;
+ static boolean mod = false, detach = false, readonly = false;
+
+ public static void accessClass(String name) throws Exception {
+ CtClass cc = cp.get(name);
+ System.out.println(cc.getName());
+ cc.getSuperclass();
+ if (mod)
+ cc.getClassFile();
+
+ if (detach)
+ cc.stopPruning(true);
+
+ if (!readonly)
+ cc.toBytecode();
+
+ if (detach)
+ cc.detach();
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static void accessAll(String filename) throws Exception {
+ ZipFile zip = new ZipFile(filename);
+ Enumeration files = zip.entries();
+ while (files.hasMoreElements()) {
+ ZipEntry z = (ZipEntry)files.nextElement();
+ String name = z.getName();
+ if (name.endsWith(".class")) {
+ name = name.substring(0, name.length() - 6)
+ .replace('/', '.');
+ accessClass(name);
+ }
+ }
+
+ zip.close();
+ }
+
+ public static void main(String[] args) throws Exception {
+ cp = ClassPool.getDefault();
+ cp.appendClassPath(args[0]);
+ if (args[1].equals("true"))
+ mod = true;
+ else if (args[1].equals("detach"))
+ mod = detach = true;
+ else if (args[1].equals("read"))
+ readonly = true;
+
+ System.err.println("mod: " + mod + " detach: " + detach
+ + " readonly: " + readonly);
+ accessAll(args[0]);
+ }
+}
diff --git a/src/test/javassist/HotswapTest.java b/src/test/javassist/HotswapTest.java
new file mode 100644
index 0000000..deab8fa
--- /dev/null
+++ b/src/test/javassist/HotswapTest.java
@@ -0,0 +1,38 @@
+package javassist;
+
+import javassist.util.HotSwapAgent;
+import junit.framework.TestCase;
+
+public class HotswapTest extends TestCase {
+ public static void main(String[] args) throws Exception {
+ // run java -javaagent:hotswap.jar javassist.HotswapTest
+ new HotswapTest(HotswapTest.class.getName()).testHotswap();
+ }
+
+ public HotswapTest(String name) {
+ super(name);
+ }
+
+ public static class Foo {
+ public int foo() { return 1; }
+ }
+
+ public void testHotswap() throws Exception {
+ if (javassist.bytecode.ClassFile.MAJOR_VERSION
+ >= javassist.bytecode.ClassFile.JAVA_9)
+ return;
+
+ Foo f = new Foo();
+ assertEquals(1, f.foo());
+
+ ClassPool cp = ClassPool.getDefault();
+ CtClass clazz = cp.get(Foo.class.getName());
+ CtMethod m = clazz.getDeclaredMethod("foo");
+ clazz.removeMethod(m);
+ clazz.addMethod(CtNewMethod.make("public int foo() { return 2; }", clazz));
+ HotSwapAgent.redefine(Foo.class, clazz);
+ Foo g = new Foo();
+ assertEquals(2, g.foo());
+ System.out.println("Foo#foo() = " + g.foo());
+ }
+}
diff --git a/src/test/javassist/JvstTest.java b/src/test/javassist/JvstTest.java
new file mode 100644
index 0000000..aab0b90
--- /dev/null
+++ b/src/test/javassist/JvstTest.java
@@ -0,0 +1,1185 @@
+package javassist;
+
+import junit.framework.*;
+import test1.DefineClassCapability;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import javassist.bytecode.*;
+import javassist.expr.*;
+import javassist.runtime.*;
+
+@SuppressWarnings({"rawtypes","unused", "resource"})
+public class JvstTest extends JvstTestRoot {
+ public static boolean java9;
+
+ static {
+ //javassist.bytecode.MethodInfo.doPreverify = true;
+ java9 = javassist.bytecode.ClassFile.MAJOR_VERSION
+ >= javassist.bytecode.ClassFile.JAVA_9;
+ }
+ public JvstTest(String name) {
+ super(name);
+ }
+
+ public void testConfig() {
+ // is the value of PATH correct?
+ assertTrue("not found " + PATH, new java.io.File(PATH).exists());
+ }
+
+ public void testLoader() throws Exception {
+ Loader loader = new Loader(sloader);
+ loader.delegateLoadingOf("test1.");
+ assertEquals(loader.loadClass("test1.Cflow").getClassLoader(),
+ loader.getParent());
+ assertEquals(loader.loadClass("javassist.Loader").getClassLoader(),
+ loader.getParent());
+ assertEquals(loader.loadClass("javassist.CtClass").getClassLoader(),
+ loader);
+ }
+
+ public void testDefreeze() throws Exception {
+ CtClass cc = sloader.get("test1.Freeze");
+ cc.stopPruning(true);
+ cc.addInterface(sloader.get("java.io.Serializable"));
+ assertTrue(!cc.isFrozen());
+ cc.writeFile();
+ assertTrue(cc.isFrozen());
+ cc.defrost();
+ assertTrue(!cc.isFrozen());
+ }
+
+ public void testClassPath() throws Exception {
+ ClassPool pool = new ClassPool(null);
+ ClassPath cp1 = pool.appendClassPath("d1");
+ ClassPath cp2 = pool.appendClassPath("d2");
+ ClassPath cp3 = pool.appendClassPath("d3");
+ ClassPath cp4 = pool.appendClassPath("d4");
+ print(pool.toString());
+ pool.removeClassPath(cp3);
+ print(pool.toString());
+ pool.removeClassPath(cp4);
+ print(pool.toString());
+ pool.removeClassPath(cp2);
+ print(pool.toString());
+ pool.removeClassPath(cp1);
+ assertTrue("[class path: ]".equals(pool.toString()));
+ }
+
+ public void testReleaseJarClassPathFileHandle() throws Exception {
+ String jarFileName = "./empty.jar";
+ ClassLoader classLoader = getClass().getClassLoader();
+ File jarFile = new File(classLoader.getResource(jarFileName).getFile());
+ assertTrue(jarFile.exists());
+
+ // Prepare class pool and force it to open the Jar file
+ ClassPool pool = ClassPool.getDefault();
+ ClassPath cp = pool.appendClassPath(jarFile.getAbsolutePath());
+ assertNull(cp.openClassfile("nothere.Dummy"));
+
+ // Assert that it is possible to delete the jar file.
+ // On Windows deleting an open file will fail, while on on Mac/Linux this is always possible.
+ // This check will thus only fail on Windows if the file is still open.
+ assertTrue(jarFile.delete());
+ }
+
+ public void testJarClassPath() throws Exception {
+ String jarFileName = "./simple.jar";
+ ClassLoader classLoader = getClass().getClassLoader();
+ File jarFile = new File(classLoader.getResource(jarFileName).getFile());
+ assertTrue(jarFile.exists());
+
+ ClassPool pool = ClassPool.getDefault();
+ ClassPath cp = pool.appendClassPath(jarFile.getAbsolutePath());
+ InputStream is = cp.openClassfile("com.test.Test");
+ assertNotNull(is);
+ is.close();
+ }
+
+ public void testSubtype() throws Exception {
+ CtClass cc = sloader.get("test1.Subtype");
+ assertTrue(cc.subtypeOf(cc));
+ assertTrue(cc.subtypeOf(sloader.get("test1.SubtypeA")));
+ assertTrue(cc.subtypeOf(sloader.get("test1.SubtypeB")));
+ assertTrue(cc.subtypeOf(sloader.get("test1.SubtypeC")));
+ assertTrue(cc.subtypeOf(sloader.get("java.lang.Object")));
+ assertTrue(!cc.subtypeOf(sloader.get("java.lang.String")));
+ }
+
+ public void testClassPoolGet() throws Exception {
+ ClassPool pool = ClassPool.getDefault();
+ CtClass cc = pool.makeClass("test1.Point");
+ CtClass cc1 = pool.get("test1.Point"); // cc1 is identical to cc.
+ cc.setName("test1.Pair");
+ CtClass cc2 = pool.get("test1.Pair"); // cc2 is identical to cc.
+ CtClass cc3 = pool.get("test1.Point"); // cc3 is not identical to cc.
+
+ assertTrue(cc == cc1);
+ assertTrue(cc == cc2);
+ assertTrue(cc != cc3);
+
+ assertEquals("test1.Pair", cc.getName());
+ assertEquals("test1.Point", cc3.getName());
+ }
+
+ public static long testFieldInitHash;
+
+ /* test CodeIterator.insertExGap().
+ * The result of this test is checked again by JvstTest3#testFieldInitAgain().
+ */
+ public void testFieldInit() throws Exception {
+ CtClass cc = sloader.get("test1.FieldInit");
+ CtField f1 = new CtField(CtClass.intType, "f1", cc);
+ cc.addField(f1, CtField.Initializer.byCall(cc, "get"));
+ CtField f2 = CtField.make("public int f2 = 3;", cc);
+ cc.addField(f2);
+ CtField f3 = CtField.make("public int f3;", cc);
+ cc.addField(f3);
+ CtField f4 = CtField.make("public int f4 = this.f2 + 3;", cc);
+ cc.addField(f4);
+ CtField fi = CtField.make("public test1.FieldInit.FI fi = new test1.FieldInit.FI(this);", cc);
+ cc.addField(fi);
+ testFieldInitHash = f1.hashCode();
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ int value = obj.getClass().getField("counter").getInt(obj);
+ assertEquals(1, value);
+ int value2 = obj.getClass().getField("f2").getInt(obj);
+ assertEquals(3, value2);
+ int value3 = obj.getClass().getField("f3").getInt(obj);
+ assertEquals(0, value3);
+ int value4 = obj.getClass().getField("f4").getInt(obj);
+ assertEquals(6, value4);
+ Object obfi = obj.getClass().getField("fi").get(obj);
+ assertTrue(obfi.getClass().getField("fi").get(obfi) == obj);
+ }
+
+ /* test CodeIterator.insertExGap().
+ */
+ public void testFieldInit2() throws Exception {
+ CtClass cc = sloader.get("test1.FieldInit2");
+ CtField f = new CtField(CtClass.intType, "f1", cc);
+ cc.addField(f, CtField.Initializer.byCall(cc, "get"));
+ cc.writeFile();
+ try {
+ Object obj = make(cc.getName());
+ fail();
+ }
+ catch (Exception e) {
+ print("testFieldInit2: catch");
+ }
+ }
+
+ public static CtMethod testCalleeBeforeMethod;
+ public static long testCalleeBeforeMethod2;
+
+ /* The test result is checked again by JvstTest3#testCalleeBeforeAgain().
+ */
+ public void testCalleeBefore() throws Exception {
+ CtClass cc = sloader.get("test1.CalleeBefore");
+
+ CtMethod m1 = cc.getDeclaredMethod("m1");
+ m1.insertBefore("{ int k = 1; p = k; }");
+ CtMethod m2 = cc.getDeclaredMethod("m2");
+ testCalleeBeforeMethod = m1;
+ testCalleeBeforeMethod2 = m2.getMethodInfo2().hashCode();
+ m2.insertBefore("{ int k = 3; q = k; }");
+ CtConstructor[] cons = cc.getDeclaredConstructors();
+
+ for (int i = 0; i < cons.length; ++i) {
+ MethodInfo minfo = cons[i].getMethodInfo();
+ CodeAttribute ca = minfo.getCodeAttribute();
+ CodeIterator iterator = ca.iterator();
+ if (cons[i].getParameterTypes().length == 0) {
+ assertTrue(iterator.skipThisConstructor() >= 0);
+ assertTrue(iterator.skipSuperConstructor() < 0);
+ assertTrue(iterator.skipConstructor() >= 0);
+ }
+ else {
+ assertTrue(iterator.skipThisConstructor() < 0);
+ assertTrue(iterator.skipSuperConstructor() >= 0);
+ assertTrue(iterator.skipConstructor() >= 0);
+ }
+
+ cons[i].insertBeforeBody("{ int k = 1; counter += k; }");
+ }
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(0, invoke(obj, "getr"));
+ assertEquals(17, invoke(obj, "test"));
+ }
+
+ public void testCalleeAfter() throws Exception {
+ CtClass cc = sloader.get("test1.CalleeAfter");
+
+ CtMethod m1 = cc.getDeclaredMethod("m1");
+ m1.insertAfter("{ int k = 1; $_ = $_ + k; }", false);
+
+ CtMethod m2 = cc.getDeclaredMethod("m2");
+ m2.insertAfter("{ char k = 1; $_ = $_ + k; }", false);
+
+ CtConstructor[] cons = cc.getDeclaredConstructors();
+ cons[0].insertAfter("{ ++p; $_ = ($r)null; }", false);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(15, invoke(obj, "test"));
+ }
+
+ public void testCalleeAfter2() throws Exception {
+ CtClass cc = sloader.get("test1.CalleeAfter2");
+
+ CtMethod m1 = cc.getDeclaredMethod("m1");
+ m1.insertAfter("$_ = 7; $_ = ($r)k1(0);", false);
+
+ CtMethod m2 = cc.getDeclaredMethod("m2");
+ m2.insertAfter("$_ = ($r)k2(0);", false);
+
+ CtMethod m3 = cc.getDeclaredMethod("m3");
+ m3.insertAfter("$_ = ($r)k3(0);", false);
+
+ CtMethod m4 = cc.getDeclaredMethod("m4");
+ try {
+ m4.insertAfter("$_ = ($r)1;", false);
+ assertTrue(false);
+ }
+ catch (CannotCompileException e) {
+ }
+
+ CtMethod m5 = cc.getDeclaredMethod("m5");
+ m5.insertAfter("$_ = ($r)k5(0);", false);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(17, invoke(obj, "test"));
+ }
+
+ public void testCalleeAfter3() throws Exception {
+ CtClass cc = sloader.get("test1.CalleeAfter3");
+ CtMethod m1 = cc.getDeclaredMethod("m1");
+ m1.insertAfter("value++;", true);
+ CtMethod m2 = cc.getDeclaredMethod("m2");
+ m2.insertAfter("value++;", true);
+ CtMethod m3 = cc.getDeclaredMethod("m3");
+ m3.insertAfter("value++;", true);
+ CtMethod m4 = cc.getDeclaredMethod("m4");
+ m4.insertAfter("value++;", true);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(22, invoke(obj, "test"));
+ }
+
+ public void testCalleeCatch() throws Exception {
+ CtClass cc = sloader.get("test1.CalleeCatch");
+
+ CtMethod m1 = cc.getDeclaredMethod("m1");
+ m1.addCatch("{ System.out.println($e); return p; }",
+ sloader.get("java.lang.Exception"));
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "test"));
+ }
+
+ public void testSuperclass() throws Exception {
+ CtClass cc = sloader.get("java.lang.Object");
+ assertEquals(null, cc.getSuperclass());
+ }
+
+ public void testProceed() throws Exception {
+ CtClass cc = sloader.get("test1.Proceed");
+
+ CtMethod m1 = CtNewMethod.make(
+ "public int m1() { return $proceed(3); }",
+ cc, "this", "k1");
+ CtMethod m2 = CtNewMethod.make(
+ "public int m2() { return $proceed(3); }",
+ cc, "another", "k2");
+ CtMethod m3 = CtNewMethod.make(
+ "public int q(int i) { return p($1 + 1, $$); }", cc);
+ cc.addMethod(m1);
+ cc.addMethod(m2);
+ cc.addMethod(m3);
+ CtMethod m4 = CtNewMethod.make(
+ "public int q2() { return q(4); }", cc);
+ cc.addMethod(m4);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "m1"));
+ assertEquals(4, invoke(obj, "m2"));
+ assertEquals(9, invoke(obj, "q2"));
+ }
+
+ public void testProceed2() throws Exception {
+ CtClass cc = sloader.get("test1.Proceed2");
+ CtMethod m1 = cc.getDeclaredMethod("k1");
+ m1.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ m.replace("{ $_ = $proceed($$); }");
+ }
+ public void edit(NewExpr m) throws CannotCompileException {
+ m.replace("{ $_ = $proceed($$); }");
+ }
+ public void edit(FieldAccess m) throws CannotCompileException {
+ m.replace("{ $_ = $proceed($$); }");
+ }
+ public void edit(Instanceof i) throws CannotCompileException {
+ i.replace("{ $_ = $proceed($$); }");
+ }
+ public void edit(Cast c) throws CannotCompileException {
+ c.replace("{ $_ = $proceed($$); }");
+ }
+ });
+
+ CtMethod m2 = cc.getDeclaredMethod("k2");
+ m2.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ m.replace("{ $proceed(); }");
+ }
+ public void edit(NewExpr m) throws CannotCompileException {
+ m.replace("{ $_ = $proceed(); }");
+ }
+ public void edit(FieldAccess m) throws CannotCompileException {
+ if (m.isReader())
+ m.replace("{ $_ = $proceed(); }");
+ else
+ m.replace("{ $proceed($$); }");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(2, invoke(obj, "k1"));
+ }
+
+ public void testProceed3() throws Exception {
+ CtClass cc = sloader.get("test1.Proceed3");
+ CtMethod m1 = cc.getDeclaredMethod("p");
+ CtMethod m2 = CtNewMethod.copy(m1, cc, null);
+ m1.setName(m1.getName() + "_orig");
+ m2.setBody("{ return $proceed($1 + 1); }", "this", m1.getName());
+ cc.addMethod(m2);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(4, invoke(obj, "k1"));
+ }
+
+ public void testSetBody() throws Exception {
+ CtClass cc = sloader.get("test1.SetBody");
+ CtMethod m1 = cc.getDeclaredMethod("m1");
+ m1.setBody("{ int i = $1 * $2; return i; }");
+ CtMethod m2 = cc.getDeclaredMethod("m2");
+ m2.setBody("System.out.println(\"setbody: \" + $1);");
+
+ CtMethod m3 = cc.getDeclaredMethod("m3");
+ try {
+ m3.setBody("value = 1; System.out.println(\"setbody: \" + $1);");
+ fail();
+ }
+ catch (CannotCompileException e) {
+ // System.err.println(e);
+ }
+
+ CtConstructor cons
+ = new CtConstructor(new CtClass[] { CtClass.intType }, cc);
+ cons.setBody(null);
+ cc.addConstructor(cons);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(12, invoke(obj, "run"));
+ }
+
+ public void testSetStaticConsBody() throws Exception {
+ CtClass cc = sloader.get("test1.StaticConsBody");
+ CtConstructor cons = cc.getClassInitializer();
+ cons.setBody(null);
+
+ cons = cc.getConstructors()[0];
+ cons.setBody(null);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(0, invoke(obj, "run"));
+ }
+
+ public void testSetConsBody() throws Exception {
+ CtClass superClazz = sloader.get("java.io.File");
+ CtClass cc = sloader.makeClass("test1.SetConsBody");
+ cc.setSuperclass(superClazz);
+ CtConstructor constructor = new CtConstructor(new CtClass[0], cc);
+ constructor.setBody("super(\"MyFile\");");
+ cc.addConstructor(constructor);
+
+ constructor = new CtConstructor(new CtClass[] { CtClass.intType },
+ cc);
+ constructor.setBody("{ super(\"MyFile\"); }");
+ cc.addConstructor(constructor);
+
+ cc.addMethod(CtNewMethod.make(CtClass.voidType, "m1",
+ null, null, null, cc));
+ cc.addMethod(CtNewMethod.make(CtClass.intType, "m2",
+ null, null, null, cc));
+ cc.addMethod(CtNewMethod.make(CtClass.byteType, "m3",
+ null, null, null, cc));
+ cc.addMethod(CtNewMethod.make(CtClass.longType, "m4",
+ null, null, null, cc));
+ cc.addMethod(CtNewMethod.make(CtClass.floatType, "m5",
+ null, null, null, cc));
+ cc.addMethod(CtNewMethod.make(CtClass.doubleType, "m6",
+ null, null, null, cc));
+ cc.addMethod(CtNewMethod.make(sloader.get("int[]"), "m7",
+ null, null, null, cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int run() {"
+ + " return (int)(m2() + m3() + m4() + m5() + m6() + 3); }", cc));
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "run"));
+ }
+
+ public void testEmptyBody() throws Exception {
+ String[] methods = { "m1", "m2", "m3", "m4" };
+ boolean[] results = { true, false, false, false, true };
+ boolean[] cResults = { true, true, false, false, false, true };
+
+ CtClass cc = sloader.get("test1.EmptyBody");
+ for (int i = 0; i < methods.length; ++i) {
+ CtMethod m = cc.getDeclaredMethod(methods[i]);
+ assertEquals(results[i], m.isEmpty());
+ }
+
+ CtConstructor[] cons = cc.getDeclaredConstructors();
+ for (int j = 0; j < cons.length; ++j)
+ assertEquals(cResults[j], cons[j].isEmpty());
+ }
+
+ public void testExprEditor() throws Exception {
+ CtClass cc = sloader.get("test1.ExprEdit");
+
+ CtMethod m1 = cc.getDeclaredMethod("k0");
+ m1.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ if (m.getClassName().equals("test1.ExprEdit")) {
+ String name = m.getMethodName();
+ if (name.equals("k1") || name.equals("k2")) {
+ try {
+ CtMethod cm = m.getMethod();
+ print(cm.getParameterTypes()[0].getName());
+ print(cm.getReturnType().getName());
+ }
+ catch (NotFoundException e) {
+ throw new CannotCompileException(e);
+ }
+ m.replace("{ ++$1; $_ = $proceed($$); }");
+ }
+ else if (name.equals("k3"))
+ m.replace("{ ++$1; $proceed($$); }");
+ }
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(12, invoke(obj, "k0"));
+ }
+
+ public void testExprEditor2() throws Exception {
+ CtClass cc = sloader.get("test1.ExprEdit2");
+
+ CtMethod m1 = cc.getDeclaredMethod("k1");
+ m1.instrument(new ExprEditor() {
+ public void edit(FieldAccess m) throws CannotCompileException {
+ if (m.getClassName().equals("test1.ExprEdit2")) {
+ String name = m.getFieldName();
+ try {
+ CtField cf = m.getField();
+ print(cf.getType().getName());
+ print("file: " + m.getFileName());
+ print("line: " + m.getLineNumber());
+ }
+ catch (NotFoundException e) {
+ throw new CannotCompileException(e);
+ }
+ if (name.equals("df"))
+ if (m.isReader())
+ m.replace("{ $_ = $proceed() + 1; }");
+ else
+ m.replace("{ $proceed($1 + 1); }");
+ else if (name.equals("sf"))
+ if (m.isReader())
+ m.replace("{ $_ = $proceed() + 2; }");
+ else
+ m.replace("{ $proceed($1 + 2); }");
+ }
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(16, invoke(obj, "k1"));
+ }
+
+ public void testExprEditor3() throws Exception {
+ CtClass cc = sloader.get("test1.ExprEdit3");
+
+ CtMethod m1 = cc.getDeclaredMethod("k1");
+ m1.instrument(new ExprEditor() {
+ public void edit(NewExpr m) throws CannotCompileException {
+ System.out.println("new " + m.getClassName());
+ try {
+ CtConstructor cc = m.getConstructor();
+ print(cc.getParameterTypes()[0].getName());
+ }
+ catch (NotFoundException e) {
+ throw new CannotCompileException(e);
+ }
+ if (m.getClassName().equals("test1.ExprEdit3")) {
+ m.replace("{ ++$2; $_ = $proceed($$); }");
+ }
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(4, invoke(obj, "k1"));
+ }
+
+ public void testExprEditor4() throws Exception {
+ CtClass cc = sloader.get("test1.ExprEdit4");
+
+ CtMethod m1 = cc.getDeclaredMethod("k1");
+ m1.instrument(new ExprEditor() {
+ public void edit(NewExpr m) throws CannotCompileException {
+ System.out.println("new " + m.getClassName());
+ if (m.getClassName().equals("test1.ExprEdit4"))
+ m.replace("$_ = null;");
+ }
+
+ public void edit(MethodCall m) throws CannotCompileException {
+ if (m.getClassName().equals("test1.ExprEdit4"))
+ m.replace("{}");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "k1"));
+ }
+
+ public void testExprEditor5() throws Exception {
+ CtClass cc = sloader.get("test1.ExprEdit5");
+
+ CtMethod m1 = cc.getDeclaredMethod("k1");
+ m1.instrument(new ExprEditor() {
+ public void edit(NewExpr m) throws CannotCompileException {
+ m.replace("{ $_ = $proceed($$, \"test\"); }");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "k1"));
+ }
+
+ public void testExprEditor6() throws Exception {
+ CtClass cc = sloader.get("test1.ExprEdit6");
+ cc.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ assertTrue(m.where().getName().equals("k1"));
+ m.replace("$_ = 3;");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "k1"));
+ }
+
+ public void testExprEditor7() throws Exception {
+ CtClass cc = sloader.get("test1.ExprEdit7");
+ cc.instrument(new ExprEditor() {
+ public void edit(Instanceof i) throws CannotCompileException {
+ i.replace("{ this.c1 = $type; $_ = !$proceed($1); }");
+ }
+ public void edit(Cast c) throws CannotCompileException {
+ c.replace("{ this.c2 = $type; $_ = ($r)$1; }");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(7, invoke(obj, "k1"));
+ }
+
+ public void testExprEditor8() throws Exception {
+ CtClass cc = sloader.get("test1.ExprEdit8");
+ cc.instrument(new ExprEditor() {
+ public void edit(ConstructorCall c) throws CannotCompileException {
+ assertTrue(c.isSuper());
+ c.replace("{ $_ = $proceed($$); value = 7; }");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(7, invoke(obj, "k1"));
+ }
+
+ public void testCflow() throws Exception {
+ CtClass cc = sloader.get("test1.Cflow");
+ CtMethod m1 = cc.getDeclaredMethod("k1");
+ m1.useCflow("cflow1");
+ m1.insertBefore("System.out.println(\"$cflow1: \" + $cflow(cflow1));");
+ m1.insertAfter("System.out.println(\"*$cflow1: \" + $cflow(cflow1));",
+ true);
+ CtMethod m2 = cc.getDeclaredMethod("k2");
+ m2.useCflow("test1.t.cflow2");
+ m2.insertBefore(
+ "System.out.println(\"$cflow2: \" + $cflow(test1.t.cflow2));");
+ CtMethod m3 = cc.getDeclaredMethod("fact");
+ m3.useCflow("fact");
+ m3.insertBefore("if ($cflow(fact) == 0)"
+ + " System.out.println(\"fact \" + $1);");
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "run"));
+ assertEquals(120, invoke(obj, "run2"));
+ }
+
+ public void testSigType() throws Exception {
+ CtClass cc = sloader.get("test1.SigType");
+
+ CtMethod m1 = cc.getDeclaredMethod("k1");
+ m1.insertBefore("{ Class[] p = $sig; $1 += p.length; }");
+ m1.insertAfter("System.out.println(\"testSigType: \""
+ + " + $type.getName());", false);
+
+ CtMethod m2 = cc.getDeclaredMethod("k2");
+ m2.instrument(new ExprEditor() {
+ public void edit(FieldAccess m) throws CannotCompileException {
+ m.replace("{ $_ = $proceed($$) + $type.getName().length(); }");
+ }
+
+ public void edit(MethodCall m) throws CannotCompileException {
+ m.replace("{ $_ = $proceed($$) + $sig.length; }");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(19, invoke(obj, "run"));
+ }
+
+ public void testDollarClass() throws Exception {
+ CtClass cc = sloader.get("test1.DollarClass");
+
+ CtMethod m1 = cc.getDeclaredMethod("k1");
+ m1.insertBefore("{ $1 += $class.getName().length(); }");
+
+ CtMethod m2 = cc.getDeclaredMethod("k2");
+ m2.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ m.replace("{ $_ = $class.getName().length(); }");
+ }
+ });
+ m2.insertBefore("{ $1 += $class.getName().length(); }");
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(58, invoke(obj, "run"));
+ }
+
+ public void testHandler() throws Exception {
+ CtClass cc = sloader.get("test1.Handler");
+
+ CtMethod m1 = cc.getDeclaredMethod("m1");
+ m1.instrument(new ExprEditor() {
+ public void edit(Handler h) throws CannotCompileException {
+ try {
+ print(h.getType().getName());
+ h.insertBefore(
+ "{ p = (($r)$1).getClass().getName().length()"
+ + "+ $type.getName().length(); }");
+ }
+ catch (NotFoundException e) {
+ throw new CannotCompileException(e);
+ }
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(("java.lang.IndexOutOfBoundsException".length()
+ + "java.lang.ClassNotFoundException".length())
+ * 2 + 1,
+ invoke(obj, "test"));
+ }
+
+ public void testInterface() throws Exception {
+ String className = "test1.NewInterface";
+ ClassPool pool = ClassPool.getDefault();
+ CtClass targetCtClass = pool.get(className);
+ CtClass ctInterface
+ = pool.makeInterface(className + "2");
+ CtMethod[] ctMethods = targetCtClass.getDeclaredMethods();
+ for (int i = 0;i < ctMethods.length; i++) {
+ String code = Modifier.toString(ctMethods[i].getModifiers())
+ + " " + ctMethods[i].getReturnType().getName()
+ + " " + ctMethods[i].getName() + "();";
+
+ System.out.println(code);
+ CtMethod m = CtNewMethod.make(code, ctInterface);
+ ctInterface.addMethod(m);
+ }
+
+ targetCtClass.addInterface(ctInterface);
+ targetCtClass.stopPruning(true);
+ targetCtClass.writeFile();
+
+ ctInterface.stopPruning(true);
+ ctInterface.writeFile();
+ ctInterface.toClass(DefineClassCapability.class);
+ targetCtClass.toClass(DefineClassCapability.class);
+ }
+
+ public void testDispatch() throws Exception {
+ CtClass cc = sloader.get("test1.Dispatch");
+
+ CtMethod m1 = cc.getDeclaredMethod("run");
+ m1.insertAfter("$_ += f(new Object[1]);");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(7, invoke(obj, "run"));
+ }
+
+ public void testMakeClass()throws Exception {
+ CtClass cc = sloader.makeClass(
+ new FileInputStream(PATH + "test1/MakeClass.class"));
+ assertEquals("test1.MakeClass", cc.getName());
+ assertEquals(cc, sloader.get(cc.getName()));
+ cc.toBytecode();
+ assertTrue(cc.isFrozen());
+ try {
+ cc = sloader.makeClass(
+ new FileInputStream(PATH + "test1/MakeClass.class"));
+ assertTrue(false);
+ }
+ catch (RuntimeException e) {
+ print(e.getMessage());
+ }
+ }
+
+ public void testMakeMethod() throws Exception {
+ CtClass cc = sloader.makeClass("test1.MakeMethod");
+ cc.addField(new CtField(CtClass.intType, "i", cc));
+ String cons_body = "{ i = 3; }";
+ CtConstructor cons = CtNewConstructor.make(null, null,
+ cons_body, cc);
+ cc.addConstructor(cons);
+ CtMethod m = CtNewMethod.make(CtClass.intType, "run", null, null,
+ "{ return i; }", cc);
+ cc.addMethod(m);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "run"));
+ }
+
+ public void testDesc() throws Exception {
+ Class[] sig;
+
+ assertEquals(int.class, Desc.getType("I"));
+ assertEquals(String.class, Desc.getType("Ljava/lang/String;"));
+ assertEquals(String[].class, Desc.getType("[Ljava/lang/String;"));
+ assertEquals(int[].class, Desc.getType("[I"));
+
+ sig = Desc.getParams("()V");
+ assertEquals(0, sig.length);
+
+ sig = Desc.getParams("(I)V");
+ assertEquals(int.class, sig[0]);
+ assertEquals(1, sig.length);
+
+ sig = Desc.getParams("(IJ)V");
+ assertEquals(long.class, sig[1]);
+ assertEquals(2, sig.length);
+
+ sig = Desc.getParams("(Ljava/lang/String;)V");
+ assertEquals(String.class, sig[0]);
+ assertEquals(1, sig.length);
+
+ sig = Desc.getParams("([Ljava/lang/String;I)V");
+ assertEquals(String[].class, sig[0]);
+ assertEquals(2, sig.length);
+
+ sig = Desc.getParams("(Ljava/lang/String;[Ljava/lang/String;)V");
+ assertEquals(String[].class, sig[1]);
+ assertEquals(2, sig.length);
+ }
+
+ public void testCast() throws Exception {
+ CtClass cc = sloader.makeClass("test1.CastTest");
+
+ StringBuffer src = new StringBuffer();
+ src.append("public void test(java.lang.String[] strValues)\n");
+ src.append("{\n");
+ src.append("\tObject[] values = new Object[2];");
+ src.append("\tvalues[0] = strValues;");
+ src.append("\tvalues[1] = strValues;");
+ src.append("\tstrValues = (String[])values[0];");
+ src.append("}\n");
+
+ CtMethod m = CtNewMethod.make(src.toString(), cc);
+ }
+
+ static final long svUID = 6006955401253799668L;
+
+ public void testSerialVUID() throws Exception {
+ CtClass cc = sloader.get("test1.MySerializableClass");
+ assertEquals(svUID, SerialVersionUID.calculateDefault(cc));
+ SerialVersionUID.setSerialVersionUID(cc);
+ cc.writeFile();
+ }
+
+ public void testInvokeInt() throws Exception {
+ CtClass cc = sloader.get("test1.InvokeInt");
+ CtMethod m1 = cc.getDeclaredMethod("check");
+
+ m1.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ m.replace("$_ = $proceed($$) + k(1);");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(6, invoke(obj, "run"));
+ }
+
+ public void testSubtypeOf() throws Exception {
+ testSubtypeOf2("java.lang.Object", "int", false);
+ testSubtypeOf2("int[]", "java.lang.Object", true);
+ testSubtypeOf2("int[]", "java.lang.Cloneable", true);
+ testSubtypeOf2("java.lang.Object", "int[]", false);
+ testSubtypeOf2("java.lang.Integer", "java.lang.Number", true);
+ testSubtypeOf2("java.lang.Number", "java.lang.Integer", false);
+ testSubtypeOf2("java.lang.Integer[]", "java.lang.Number[]", true);
+ testSubtypeOf2("java.lang.Number[]", "java.lang.Integer[]", false);
+ testSubtypeOf2("java.lang.Integer", "java.io.Serializable", true);
+ testSubtypeOf2("java.lang.Integer", "java.lang.Object", true);
+ }
+
+ private void testSubtypeOf2(String s, String t, boolean b)
+ throws Exception
+ {
+ assertTrue(sloader.get(s).subtypeOf(sloader.get(t)) == b);
+ }
+
+ public void testMakeInterface() throws Exception {
+ CtClass cc = sloader.makeInterface("test1.MkInterface");
+ CtMethod m = CtNewMethod.make("public abstract void ready();", cc);
+ cc.addMethod(m);
+ cc.writeFile();
+ // cloader.loadClass(cc.getName());
+ java.io.File genDir = new java.io.File(".");
+ java.net.URLClassLoader ucl = new java.net.URLClassLoader(
+ new java.net.URL[] { genDir.toURI().toURL() }, null);
+ Class intf = ucl.loadClass("test1.MkInterface");
+ }
+
+ public void testCodeConv() throws Exception {
+ CtClass cc = sloader.get("test1.CodeConv");
+ CtClass pc = sloader.get("test1.CodeConvP");
+
+ CodeConverter conv = new CodeConverter();
+ conv.replaceFieldRead(pc.getDeclaredField("a1"), cc, "getA1");
+ conv.replaceFieldRead(pc.getDeclaredField("a2"), cc, "getA2");
+ conv.redirectFieldAccess(pc.getDeclaredField("a3"), cc, "a4");
+ conv.replaceFieldWrite(pc.getDeclaredField("b1"), cc, "putB1");
+
+ cc.instrument(conv);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(51, invoke(obj, "run"));
+ }
+
+ public void testTryCatch() throws Exception {
+ CtClass cc = sloader.get("test1.TryCatch");
+ CtMethod m1 = cc.getDeclaredMethod("m1");
+
+ m1.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ m.replace(
+ "try { doit(); }"
+ + "catch(NullPointerException e){ init(); doit(); }");
+ }
+ });
+
+ final String src =
+ "try { doit(); }"
+ + "catch(NullPointerException e){ init(); doit(); return a; }";
+
+ CtMethod p1 = cc.getDeclaredMethod("p1");
+ p1.insertAfter(src, true);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(4, invoke(obj, "run"));
+ Object obj2 = make(cc.getName());
+ assertEquals(4, invoke(obj2, "p1"));
+ }
+
+ private CtClass[] throwablesList = null;
+
+ public void testGetThrowables() throws Exception {
+ CtClass cc = sloader.get("test1.GetThrowables");
+ CtMethod m1 = cc.getDeclaredMethod("run");
+ m1.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ throwablesList = m.mayThrow();
+ }
+ });
+
+ System.out.println(throwablesList[0].getName());
+ System.out.println(throwablesList[1].getName());
+ assertEquals(2, throwablesList.length);
+ }
+
+ public void testArrayAccess() throws Exception {
+ CtClass cc = sloader.get("test1.ArrayAccess");
+ CtMethod m1 = cc.getDeclaredMethod("test");
+ m1.insertBefore("{ ia[0] += 1; iaa[1] = iaa[0]; }");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(8, invoke(obj, "test"));
+ }
+
+ public void testClinit() throws Exception {
+ CtClass cc = sloader.get("test1.Clinit");
+ CtConstructor clinit = cc.getClassInitializer();
+ assertTrue(clinit != null);
+ try {
+ clinit.insertBeforeBody(";");
+ assertTrue(false);
+ }
+ catch (CannotCompileException e) {
+ print(e.toString());
+ assertEquals("class initializer", e.getReason());
+ }
+
+ CtConstructor[] init = cc.getConstructors();
+ assertEquals(1, init.length);
+ clinit.insertAfter("j += 1;");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(457, invoke(obj, "run"));
+ }
+
+ public void testClinit2() throws Exception {
+ CtClass cc = sloader.get("test1.Clinit2");
+ CtConstructor clinit = cc.makeClassInitializer();
+ clinit.insertAfter("j = 7;");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(7, invoke(obj, "run"));
+ }
+
+ // by yamazaki
+ public void testCondExpr() throws Exception {
+ CtClass cc = sloader.makeClass("test1.CondExpr");
+ CtMethod methodM = new CtMethod(CtClass.intType, "m",
+ new CtClass[]{ CtClass.intType }, cc);
+ methodM.setModifiers(methodM.getModifiers() | Modifier.STATIC);
+ methodM.setBody("{if($1 <= 0) return 1; else return 0;}");
+ cc.addMethod(methodM);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(0, invoke(obj, "m", 3));
+ }
+
+ // by yamazaki
+ public void testCondExpr2() throws Exception {
+ CtClass cc = sloader.makeClass("test1.CondExpr2");
+ CtMethod methodM = new CtMethod(CtClass.intType, "m",
+ new CtClass[]{ CtClass.intType }, cc);
+ methodM.setModifiers(methodM.getModifiers() | Modifier.STATIC);
+ methodM.setBody("{return ($1 <= 0) ? 1 : (m($1 - 1) * $1);}");
+ cc.addMethod(methodM);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(6, invoke(obj, "m", 3));
+ }
+
+ // by yamazaki
+ public void testCondExpr3() throws Exception {
+ CtClass cc = sloader.makeClass("test1.CondExpr3");
+
+ CtMethod methodM = CtNewMethod.make(
+ "public abstract int m(int i);", cc);
+ CtMethod methodN = CtNewMethod.make(
+ "public abstract int n(int i);", cc);
+ cc.addMethod(methodM);
+ cc.addMethod(methodN);
+
+ methodM.setBody("{return ($1 <= 0) ? 1 : (n($1 - 1) * $1);}");
+ methodN.setBody("{return m($1);}");
+
+ cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(6, invoke(obj, "m", 3));
+ }
+
+ public void testDelegator() throws Exception {
+ CtClass cc = sloader.get("test1.Delegator");
+
+ assertEquals("test1.SuperDelegator", cc.getSuperclass().getName());
+
+ CtMethod f = sloader.getMethod("test1.SuperDelegator", "f");
+ CtMethod g = sloader.getMethod("test1.SuperDelegator", "g");
+
+ cc.addMethod(CtNewMethod.delegator(f, cc));
+ cc.addMethod(CtNewMethod.delegator(g, cc));
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(15, invoke(obj, "run"));
+ }
+
+ public void testSetName() throws Exception {
+ CtClass cc = sloader.get("test1.SetName");
+ CtMethod m0 = cc.getDeclaredMethod("foo");
+ cc.setName("test1.SetName2");
+ assertEquals(cc, sloader.get("test1.SetName2"));
+ assertEquals("foo(Ltest1/SetName2;)", m0.getStringRep());
+ CtClass cc2 = sloader.makeClass("test1.SetName3");
+ CtMethod m = CtNewMethod.make(
+ "public int m(test1.SetName2 obj) { return ((test1.SetName2)obj).i; }",
+ cc2);
+ cc2.addMethod(m);
+ cc.writeFile();
+ cc2.writeFile();
+ }
+
+ public void testFieldModifier() throws Exception {
+ CtClass cc = sloader.get("test1.FieldMod");
+ CtField f = cc.getField("text");
+ f.setModifiers(Modifier.PUBLIC);
+ f = cc.getField("i");
+ f.setName("j");
+ cc.writeFile();
+
+ Object obj = make(cc.getName());
+ assertEquals(java.lang.reflect.Modifier.PUBLIC,
+ obj.getClass().getField("text").getModifiers());
+ assertTrue(obj.getClass().getField("j") != null);
+ }
+
+ public void testToString() throws Exception {
+ System.out.println(sloader.get("test1.FieldMod"));
+ System.out.println(sloader.get("java.lang.Object"));
+ }
+
+ public void testPackage() throws Exception {
+ Object obj = new Loader().loadClass("test1.Pac").getConstructor().newInstance();
+ assertEquals(1, invoke(obj, "run"));
+ }
+
+ public void testHoward() throws Exception {
+ String head =
+ "public Object lookup() throws java.rmi.RemoteException ";
+ String src =
+ "{ if (_remote != null) return _remote;"
+ + " test1.HowardHome h = (test1.HowardHome)lookup(\"Howard\");"
+ + " try { _remote = h.create(); }"
+ + " catch (java.io.IOException e) { throw new java.rmi.RemoteException(e.getMessage(), e); }"
+ + " return _remote; }";
+
+ String src2 =
+ "public void lookup2() {"
+ + " try {}"
+ + " catch (java.io.IOException e) { throw new Exception(e); }"
+ + "}";
+
+ CtClass cc = sloader.get("test1.Howard");
+ CtMethod m = CtNewMethod.make(head + src, cc);
+ cc.addMethod(m);
+ try {
+ CtMethod m2 = CtNewMethod.make(src2, cc);
+ cc.addMethod(m2);
+ assertTrue(false);
+ }
+ catch (CannotCompileException e) {}
+
+ m = new CtMethod(sloader.get("java.lang.Object"),
+ "lookup3", null, cc);
+ m.setBody(src);
+ m.setModifiers(Modifier.PUBLIC);
+ m.setExceptionTypes(new CtClass[] {
+ sloader.get("java.rmi.RemoteException") });
+ cc.addMethod(m);
+
+ cc.writeFile();
+ Object target = make(cc.getName());
+ Method mth = target.getClass().getMethod("lookup", new Class[0]);
+ Object res = mth.invoke(target, new Object[0]);
+ assertEquals("howard4", res);
+
+ mth = target.getClass().getMethod("lookup3", new Class[0]);
+ res = mth.invoke(target, new Object[0]);
+ assertEquals("howard4", res);
+ }
+
+ public void testLoop() throws Exception {
+ CtClass cc = sloader.makeClass("test1.Loop");
+ CtMethod m = CtNewMethod.make(
+ "public int run(int i) { int k = 0;"
+ + "while (true) { if (k++ > 10) return i; } }",
+ cc);
+ cc.addMethod(m);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "run", 3));
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Javassist Tests");
+ suite.addTestSuite(JvstTest.class);
+ suite.addTestSuite(JvstTest2.class);
+ suite.addTestSuite(JvstTest3.class);
+ suite.addTestSuite(JvstTest4.class);
+ suite.addTestSuite(JvstTest5.class);
+ suite.addTestSuite(LoaderTestByRandall.class);
+ suite.addTestSuite(javassist.bytecode.BytecodeTest.class);
+ suite.addTestSuite(javassist.bytecode.StackMapTest.class);
+ suite.addTestSuite(javassist.compiler.CompTest.class);
+ suite.addTestSuite(javassist.SetterTest.class);
+ suite.addTestSuite(javassist.bytecode.InsertGap0.class);
+ suite.addTestSuite(javassist.tools.reflect.LoaderTest.class);
+ suite.addTestSuite(javassist.tools.CallbackTest.class);
+ suite.addTestSuite(testproxy.ProxyTester.class);
+ suite.addTestSuite(testproxy.ProxyFactoryPerformanceTest.class); // remove?
+ suite.addTestSuite(javassist.proxyfactory.ProxyFactoryTest.class);
+ suite.addTestSuite(javassist.proxyfactory.Tester.class);
+ suite.addTestSuite(javassist.HotswapTest.class);
+ suite.addTestSuite(test.javassist.proxy.ProxySerializationTest.class);
+ suite.addTestSuite(test.javassist.convert.ArrayAccessReplaceTest.class);
+ suite.addTestSuite(test.javassist.proxy.JASSIST113RegressionTest.class);
+ suite.addTestSuite(test.javassist.proxy.JBPAPP9257Test.class);
+ suite.addTestSuite(test.javassist.proxy.ProxyCacheGCTest.class); // remvoe?
+ suite.addTestSuite(test.javassist.proxy.ProxyFactoryCompatibilityTest.class);
+ suite.addTestSuite(test.javassist.proxy.ProxySerializationTest.class);
+ suite.addTestSuite(test.javassist.proxy.ProxySimpleTest.class);
+ suite.addTestSuite(test.javassist.bytecode.analysis.AnalyzerTest.class);
+ suite.addTestSuite(test.javassist.convert.ArrayAccessReplaceTest.class);
+ suite.addTestSuite(test.javassist.bytecode.analysis.DomTreeTest.class);
+ return suite;
+ }
+}
diff --git a/src/test/javassist/JvstTest2.java b/src/test/javassist/JvstTest2.java
new file mode 100644
index 0000000..0ea4571
--- /dev/null
+++ b/src/test/javassist/JvstTest2.java
@@ -0,0 +1,1530 @@
+package javassist;
+
+import java.io.*;
+import java.net.URL;
+
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+
+import java.lang.reflect.Method;
+
+import javassist.expr.*;
+import test2.DefineClassCapability;
+
+@SuppressWarnings({"rawtypes","unused"})
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class JvstTest2 extends JvstTestRoot {
+ public JvstTest2(String name) {
+ super(name);
+ }
+
+ public void testInsertAt() throws Exception {
+ CtClass cc = sloader.get("test2.InsertAt");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ int line = 6;
+ int ln = m1.insertAt(line, false, null);
+ int ln2 = m1.insertAt(line, "counter++;");
+ assertEquals(ln, ln2);
+ assertEquals(7, ln2);
+
+ line = 8;
+ ln = m1.insertAt(line, false, null);
+ ln2 = m1.insertAt(line, "counter++;");
+ assertEquals(ln, ln2);
+ assertEquals(8, ln2);
+
+ CtMethod m2 = cc.getDeclaredMethod("bar2");
+ int ln3 = m2.insertAt(20, "{ int m = 13; j += m; }");
+ assertEquals(20, ln3);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(7, invoke(obj, "foo"));
+ assertEquals(25, invoke(obj, "bar"));
+ }
+
+ public void testInsertLocal() throws Exception {
+ CtClass cc = sloader.get("test2.InsertLocal");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ m1.insertBefore("{ i = s.length(); d = 0.14; }");
+ m1.insertAfter("{ field = i; }");
+
+ CtMethod m2 = cc.getDeclaredMethod("run2");
+ m2.insertAt(22, "{ s = \"12\"; k = 5; }");
+
+ CtMethod m3 = cc.getDeclaredMethod("run3");
+ m3.instrument(new ExprEditor() {
+ public void edit(NewExpr n) throws CannotCompileException {
+ n.replace("{ i++; $_ = $proceed($$); }");
+ }
+ public void edit(FieldAccess f) throws CannotCompileException {
+ f.replace("{ i++; $_ = $proceed($$); }");
+ }
+ public void edit(MethodCall m) throws CannotCompileException {
+ m.replace("{ i++; $_ = $proceed($$); }");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(317, invoke(obj, "run"));
+ assertEquals(7, invoke(obj, "run2"));
+ assertEquals(3, invoke(obj, "run3"));
+ }
+
+ public void testStaticMember() throws Exception {
+ CtClass cc = sloader.get("test2.StaticMember");
+ CtMethod m = CtNewMethod.make(
+ "public int run() {" +
+ "return test2.StaticMember#k + test2.StaticMember#foo(); }", cc);
+ cc.addMethod(m);
+
+ m = CtNewMethod.make(
+ "public int run2() {" +
+ "return k + foo(); }", cc);
+ cc.addMethod(m);
+
+ m = CtNewMethod.make(
+ "public int run3() {" +
+ " test2.StaticMember sm = this;" +
+ " return sm.k + sm.foo(); }", cc);
+ cc.addMethod(m);
+
+ m = CtNewMethod.make(
+ "public int run4() {" +
+ " return this.k + this.foo(); }", cc);
+ cc.addMethod(m);
+
+ m = CtNewMethod.make(
+ "public static int run5() {" +
+ " return k + foo(); }", cc);
+ cc.addMethod(m);
+
+ m = CtNewMethod.make(
+ "public int run6() {" +
+ " test2.IStaticMember i = this; return i.bar(); }", cc);
+ cc.addMethod(m);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(10, invoke(obj, "run"));
+ assertEquals(10, invoke(obj, "run2"));
+ assertEquals(10, invoke(obj, "run3"));
+ assertEquals(10, invoke(obj, "run4"));
+ assertEquals(10, invoke(obj, "run5"));
+ assertEquals(3, invoke(obj, "run6"));
+ }
+
+ public void testStaticMember2() throws Exception {
+ CtClass cc = sloader.get("test2.StaticMember2");
+
+ cc.addMethod(CtNewMethod.make(
+ "public int run() {"
+ + " return test2.StaticMember2.k + test2.StaticMember2.seven()"
+ + " + (test2.StaticMember2.f + f)"
+ + " + test2.StaticMember2.f + f; }",
+ cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int run1() {"
+ + " long j = 1L;"
+ + " return (int)(j + (test2.StaticMember2.fj + fj)"
+ + " + test2.StaticMember2.fj + fj); }",
+ cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int run2() {"
+ + " double x = 1.0;"
+ + " double d = x + test2.StaticMember2.fd + fd"
+ + " + (test2.StaticMember2.fd + fd);"
+ + " return (int)(d * 10); }",
+ cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int run3() {"
+ + " return (test2.StaticMember2.fb & fb) ? 1 : 0; }",
+ cc));
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(54, invoke(obj, "run"));
+ assertEquals(53, invoke(obj, "run1"));
+ assertEquals(958, invoke(obj, "run2"));
+ assertEquals(0, invoke(obj, "run3"));
+ }
+
+ public void testSuperCall() throws Exception {
+ CtClass cc = sloader.get("test2.SuperCall");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ m1.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ m.replace("{ $_ = $proceed($$); }");
+ }
+ });
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ invoke(obj, "bar");
+ }
+
+ public void testSetSuper() throws Exception {
+ CtClass cc = sloader.makeClass("test2.SetSuper");
+ CtClass cc2 = sloader.makeClass("test2.SetSuperParent");
+ CtClass intf = sloader.makeInterface("test2.SetSuperIntf");
+ CtClass remote = sloader.get("java.rmi.Remote");
+
+ cc.setSuperclass(cc2);
+ cc.setInterfaces(new CtClass[] { intf });
+ intf.setSuperclass(remote);
+ intf.writeFile();
+ cc2.writeFile();
+ cc.writeFile();
+
+ assertEquals(cc2, cc.getSuperclass());
+ assertEquals(intf, cc.getInterfaces()[0]);
+ assertEquals(sloader.get("java.lang.Object"), intf.getSuperclass());
+ assertEquals(remote, intf.getInterfaces()[0]);
+
+ make(cc.getName());
+ }
+
+ public void testReplaceClassName() throws Exception {
+ String oldName = "test2.ReplaceClassName2";
+ String newName = "test2.ReplaceClassName3";
+ CtClass cc = sloader.get("test2.ReplaceClassName");
+ cc.replaceClassName(oldName, newName);
+ cc.writeFile();
+ CtClass cc2 = dloader.get(cc.getName());
+ CtMethod m = cc2.getDeclaredMethod("foo");
+ assertEquals(newName, m.getParameterTypes()[0].getName());
+ }
+
+ public void testCodeGen() throws Exception {
+ CtClass cc = sloader.get("test2.CodeGen");
+ CtMethod m1 = cc.getDeclaredMethod("run");
+ m1.insertBefore(
+ "{ double d = true ? 1 : 0.1; "
+ + " d = d > 0.5 ? 0.0 : - 1.0; "
+ + " System.out.println(d); "
+ + " String s = \"foo\"; "
+ + " s = 1 + 2 + s + \"bar\"; "
+ + " s += \"poi\" + 3 + seven() + seven(\":\" + ' '); "
+ + " s += .14; "
+ + " msg = s; "
+ + " System.out.println(s); }");
+
+ // recursive type check is done if $proceed is used.
+ CtMethod m2 = CtNewMethod.make(
+ "public int test() {"
+ + " String s = $proceed(\"int\" + (3 + 0.14)) + '.'; "
+ + " System.out.println(s); return s.length(); }",
+ cc, "this", "seven");
+ cc.addMethod(m2);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(19, invoke(obj, "run"));
+ assertEquals(9, invoke(obj, "test"));
+ }
+
+ public void testCodeGen2() throws Exception {
+ CtClass cc = sloader.makeClass("test2.CodeGen2");
+
+ CtMethod m1 = CtNewMethod.make(
+ "public int test() {"
+ + " int len;"
+ + " String s = \"foo\" + \"bar\" + 3;"
+ + " System.out.println(s); len = s.length();"
+ + " len = -3 + len; len = len - (7 - 2 + -1);"
+ + " int k = 3; len += ~k - ~3;"
+ + " return len; }",
+ cc);
+ cc.addMethod(m1);
+
+ CtMethod m2 = CtNewMethod.make(
+ "public int test2() {"
+ + " double d = 0.2 - -0.1;"
+ + " d += (0.2 + 0.3) * 1.0;"
+ + " return (int)(d * 10); }",
+ cc);
+ cc.addMethod(m2);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(0, invoke(obj, "test"));
+ assertEquals(8, invoke(obj, "test2"));
+ }
+
+ // not used anymore.
+ private void notTestGetInner() throws Exception {
+ ClassPool pool = ClassPool.getDefault();
+ CtClass c = pool.get("javassist.CtMethod.ConstParameter");
+ CtClass d = pool.get("javassist.CtMethod.ConstParameter");
+ CtClass e = pool.get("javassist.CtMethod$ConstParameter");
+ assertSame(c, d);
+ assertSame(c, e);
+ try {
+ c = pool.get("test2.Inner.Fake");
+ fail("found not-existing class");
+ }
+ catch (NotFoundException ex) {}
+ }
+
+ public void testInner() throws Exception {
+ ClassPool pool = ClassPool.getDefault();
+ String classname = "test2.Inner";
+ CtClass target = pool.get(classname);
+ String src =
+ "public void sampleMethod() throws Exception {"
+ + "java.util.Properties props = new java.util.Properties();"
+ + "java.rmi.activation.ActivationGroupDesc.CommandEnvironment ace "
+ + " = null;"
+ + "java.rmi.activation.ActivationGroupDesc agd "
+ + " = new java.rmi.activation.ActivationGroupDesc(props,ace);}";
+ CtMethod newmethod = CtNewMethod.make(src, target);
+ target.addMethod(newmethod);
+
+ String src2 =
+ "public java.lang.Character.Subset sampleMethod2() {"
+ + " java.lang.Character.Subset s "
+ + " = Character.UnicodeBlock.HIRAGANA; "
+ + " return s; }";
+ CtMethod newmethod2 = CtNewMethod.make(src2, target);
+ target.addMethod(newmethod2);
+
+ target.writeFile();
+ }
+
+ public void testURL() throws Exception {
+ String url;
+
+ ClassPool cp = new ClassPool(null);
+ cp.appendSystemPath();
+
+ url = cp.find("java.lang.Object").toString();
+ System.out.println(url);
+ if (JvstTest.java9)
+ assertEquals("jrt:/java.base/java/lang/Object.class", url);
+ else {
+ assertTrue(url.startsWith("jar:file:"));
+ assertTrue(url.endsWith(".jar!/java/lang/Object.class"));
+ }
+
+ assertNull(cp.find("class.not.Exist"));
+
+ cp = new ClassPool(null);
+ cp.insertClassPath(".");
+
+ url = cp.find("test2.Inner").toString();
+ System.out.println("testURL: " + url);
+ assertTrue(url.startsWith("file:/"));
+ assertTrue(url.endsWith("/test2/Inner.class"));
+
+ assertNull(cp.find("test2.TestURL"));
+
+ cp = new ClassPool(null);
+ cp.insertClassPath(JAR_PATH + "javassist.jar");
+
+ url = cp.find("javassist.CtClass").toString();
+ System.out.println("testURL: " + url);
+ assertTrue(url.startsWith("jar:file:"));
+ assertTrue(url.endsWith("javassist.jar!/javassist/CtClass.class"));
+
+ assertNull(cp.find("javassist.TestURL"));
+
+ cp = new ClassPool(null);
+ cp.insertClassPath(new LoaderClassPath(cloader));
+
+ url = cp.find("javassist.CtMethod").toString();
+ System.out.println("testURL: " + url);
+ assertTrue(url.startsWith("file:"));
+ assertTrue(url.endsWith("/javassist/CtMethod.class"));
+
+ assertNull(cp.find("javassist.TestURL"));
+
+ cp = new ClassPool(null);
+ cp.insertClassPath(new ByteArrayClassPath("test2.ByteArray", null));
+
+ url = cp.find("test2.ByteArray").toString();
+ System.out.println("testURL: " + url);
+ assertTrue(
+ url.equals("file:/ByteArrayClassPath/test2/ByteArray.class"));
+
+ assertNull(cp.find("test2.TestURL"));
+ }
+
+ public void not_testURLClassPath() throws Exception {
+ String host = "www.csg.is.titech.ac.jp";
+ String path = "/~chiba/tmp/";
+ String url;
+
+ ClassPool cp = new ClassPool(null);
+ cp.insertClassPath(new URLClassPath(host, 80, path, "test"));
+
+ url = cp.find("test.TestClassPath").toString();
+ System.out.println(url);
+ assertEquals("http://" + host + ":80" + path
+ + "test/TestClassPath.class", url);
+
+ assertNull(cp.find("test.No"));
+ }
+
+ public void testGetURL() throws Exception {
+ CtClass cc = sloader.get("java.lang.String");
+ String url = cc.getURL().toString();
+ System.out.println(url);
+ if (JvstTest.java9) {
+ assertEquals("jrt:/java.base/java/lang/String.class", url);
+ }
+ else {
+ assertTrue(url.startsWith("jar:file:"));
+ assertTrue(url.endsWith(".jar!/java/lang/String.class"));
+ }
+
+ cc = sloader.get("int");
+ try {
+ URL u = cc.getURL();
+ fail();
+ }
+ catch (NotFoundException e) {
+ assertEquals("int", e.getMessage());
+ }
+ }
+
+ public void testInheritance() throws Exception {
+ ClassPool pool = ClassPool.getDefault();
+ String classname = "test2.Inherit";
+ CtClass target = pool.get(classname);
+ String src =
+ "public void sampleMethod() {" +
+ " test2.Inherit i = new test2.Inherit();" +
+ " test2.Inherit2 i2 = i;" +
+ " test2.Inherit3 i3 = i;" +
+ " i3.foo2(); i3.foo2(); i2.foo1(); }";
+
+ CtMethod newmethod = CtNewMethod.make(src, target);
+ target.addMethod(newmethod);
+ target.writeFile();
+ }
+
+ public void testIncOp() throws Exception {
+ CtClass target = sloader.makeClass("test2.IncOp");
+ String src =
+ "public int sample() {"
+ + " int ia[] = new int[50];"
+ + " ia[0] = 1;"
+ + " int v = ++(ia[0]);"
+ + " return v; }";
+
+ CtMethod newmethod = CtNewMethod.make(src, target);
+ target.addMethod(newmethod);
+ target.writeFile();
+ Object obj = make(target.getName());
+ assertEquals(2, invoke(obj, "sample"));
+ }
+
+ public void testSetExceptions() throws Exception {
+ CtClass cc = sloader.get("test2.SetExceptions");
+ CtMethod m = cc.getDeclaredMethod("f");
+ CtClass ex = m.getExceptionTypes()[0];
+ assertEquals("java.lang.Exception", ex.getName());
+ m.setExceptionTypes(null);
+ assertEquals(0, m.getExceptionTypes().length);
+ m.setExceptionTypes(new CtClass[0]);
+ assertEquals(0, m.getExceptionTypes().length);
+ m.setExceptionTypes(new CtClass[] { ex });
+ assertEquals(ex, m.getExceptionTypes()[0]);
+ }
+
+ public void testNullArg() throws Exception {
+ CtClass cc = sloader.makeClass("test2.NullArgTest");
+ CtMethod m1 = CtNewMethod.make(
+ "public Object foo(Object[] obj, int idx) throws Throwable {" +
+ " return null; }", cc);
+ cc.addMethod(m1);
+ CtMethod m2 = CtNewMethod.make(
+ "public void bar() { this.foo(null, 0); }", cc);
+ cc.addMethod(m2);
+ CtMethod m3 = CtNewMethod.make(
+ "public void bar2() { this.foo((Object[])null, 0); }", cc);
+ cc.addMethod(m3);
+ cc.writeFile();
+ }
+
+ public void testAddMethod() throws Exception {
+ CtClass cc = sloader.get("test2.AddMethod");
+ CtMethod m = CtNewMethod.make(
+ "public int f() { return 1; }", cc);
+ try {
+ cc.addMethod(m);
+ fail();
+ }
+ catch (CannotCompileException e) {}
+ CtMethod m2 = CtNewMethod.make(
+ "public void f(int i, int j) { return 1; }", cc);
+ cc.addMethod(m2);
+ try {
+ cc.addField(new CtField(CtClass.longType, "f", cc));
+ fail();
+ }
+ catch (CannotCompileException e) {}
+ }
+
+ public void testCopyStream() throws Exception {
+ int[] size = { 100, 4096, 8000, 1023, 1024, 1025, 2047,
+ 4096*3, 4096*6, 4096*6-1, 4096*256*3, 4096*256*6 };
+ for (int i = 0; i < size.length; i++) {
+ byte[] data = new byte[size[i]];
+ for (int j = 0; j < data.length; j++)
+ data[j] = (byte)j;
+
+ InputStream ins = new ByteArrayInputStream(data);
+ ByteArrayOutputStream outs = new ByteArrayOutputStream();
+ ClassPoolTail.copyStream(ins, outs);
+ byte[] data2 = outs.toByteArray();
+ if (data2.length != data.length)
+ throw new Exception("bad size");
+
+ for (int k = 0; k < data.length; k++)
+ if (data[k] != data2[k])
+ throw new Exception("bad element");
+ }
+ }
+
+ public void testDeclaringClass() throws Exception {
+ try {
+ CtClass cc = sloader.get("test2.NotExistingClass");
+ }
+ catch (NotFoundException e) { System.out.println(e); }
+ CtClass inner = sloader.get("test2.Nested$Inner");
+ CtClass outer = sloader.get("test2.Nested");
+ assertEquals(outer, inner.getDeclaringClass());
+ assertEquals(null, outer.getDeclaringClass());
+ assertEquals(null, CtClass.intType.getDeclaringClass());
+
+ CtClass inner3 = sloader.get("test2.Nested$Inner3");
+ outer.writeFile();
+ try {
+ CtMethod m = CtNewMethod.make(
+ "public void f(test2.Nested n) { return n.geti(); }",
+ inner3);
+ fail();
+ }
+ catch (RuntimeException e) {}
+ outer.defrost();
+ CtMethod m = CtNewMethod.make(
+ "public int f(test2.Nested n) { " +
+ "return n.geti() + test2.Nested.getj(1) + f() + g(); } ",
+ inner3);
+ inner3.addMethod(m);
+ inner3.writeFile();
+ outer.writeFile();
+
+ Object nobj = make(outer.getName());
+ Object iobj = make(inner3.getName());
+ Method mth = iobj.getClass().getMethod("f",
+ new Class[] { nobj.getClass() });
+ Object resobj = mth.invoke(iobj, new Object[] { nobj });
+ int res = ((Integer) resobj).intValue();
+ assertEquals(6, res);
+ }
+
+ public void testDeclaringClass2() throws Exception {
+ CtClass out = sloader.get("test2.Anon");
+ CtClass inner = sloader.get("test2.Anon$1");
+ if (System.getProperty("java.vm.version").startsWith("1.4"))
+ assertTrue(inner.getEnclosingBehavior() == null);
+ else {
+ assertEquals("make", inner.getEnclosingBehavior().getName());
+ assertEquals(out, inner.getDeclaringClass());
+ assertEquals(out,
+ inner.getEnclosingBehavior().getDeclaringClass());
+ }
+
+ assertNull(out.getEnclosingBehavior());
+ assertNull(out.getEnclosingBehavior());
+
+ CtClass inner2 = sloader.get("test2.Anon$Anon2$1");
+ assertTrue(inner2.getEnclosingBehavior() instanceof CtConstructor);
+ assertEquals(sloader.get("test2.Anon$Anon2"), inner2.getEnclosingBehavior().getDeclaringClass());
+ CtClass inner3 = sloader.get("test2.Anon$Anon3$1");
+ assertTrue(inner3.getEnclosingBehavior() instanceof CtConstructor);
+ assertTrue(((CtConstructor)inner3.getEnclosingBehavior()).isClassInitializer());
+ assertEquals(sloader.get("test2.Anon$Anon3"), inner3.getEnclosingBehavior().getDeclaringClass());
+ }
+
+ public void testMethodInInner() throws Exception {
+ CtClass inner = sloader.get("test2.Nested2$Inner");
+ CtClass outer = sloader.get("test2.Nested2");
+ String src =
+ "public int f(test2.Nested2 n) {" +
+ " n.i = 1; n.i++; n.i += 2; return n.i; }";
+
+ outer.writeFile();
+ try {
+ CtMethod m = CtNewMethod.make(src, inner);
+ fail();
+ }
+ catch (RuntimeException e) {}
+ outer.defrost();
+
+ CtMethod m = CtNewMethod.make(src, inner);
+ inner.addMethod(m);
+
+ src = "public int g(test2.Nested2 n) {" +
+ " n.d = 1.0; n.d++; n.d += 2.0;" +
+ " return n.d == 4.0 ? 7 : 8; }";
+ m = CtNewMethod.make(src, inner);
+ inner.addMethod(m);
+
+ src = "public int h(test2.Nested2 n) {" +
+ " n.s = \"poi\";" +
+ "return n.s.length() + f(n) + g(n); }";
+ m = CtNewMethod.make(src, inner);
+ inner.addMethod(m);
+
+ inner.writeFile();
+ outer.writeFile();
+
+ Object nobj = make(outer.getName());
+ Object iobj = make(inner.getName());
+ Method mth = iobj.getClass().getMethod("h",
+ new Class[] { nobj.getClass() });
+ Object resobj = mth.invoke(iobj, new Object[] { nobj });
+ int res = ((Integer) resobj).intValue();
+ assertEquals(14, res);
+ }
+
+ public void testMethodInInner2() throws Exception {
+ CtClass inner = sloader.get("test2.Nested3$Inner");
+ CtClass outer = sloader.get("test2.Nested3");
+ String src =
+ "public int f() {" +
+ " int k = 0;" +
+ " test2.Nested3 n = new test2.Nested3(3);" +
+ " k += n.geti();" +
+ " n = new test2.Nested3();" +
+ " k += n.geti();" +
+ " n = new test2.Nested3(\"foo\");" +
+ " k += n.geti();" +
+ " return k; }";
+
+ outer.stopPruning(true);
+ outer.writeFile();
+ try {
+ CtMethod m = CtNewMethod.make(src, inner);
+ fail();
+ }
+ catch (RuntimeException e) {}
+ outer.defrost();
+
+ CtMethod m = CtNewMethod.make(src, inner);
+ inner.addMethod(m);
+
+ inner.writeFile();
+ outer.writeFile();
+
+ Object iobj = make(inner.getName());
+ assertEquals(6, invoke(iobj, "f"));
+ }
+
+ public void testMakeNestedClass() throws Exception {
+ CtClass outer = sloader.get("test2.Nested4");
+ try {
+ CtClass inner = outer.makeNestedClass("Inner", false);
+ fail();
+ }
+ catch (RuntimeException e) {
+ print(e.getMessage());
+ }
+
+ CtClass nested = outer.makeNestedClass("Inner", true);
+ outer.stopPruning(true);
+ outer.writeFile();
+ outer.defrost();
+ String src =
+ "public int f() { return test2.Nested4.value; }";
+
+ CtMethod m = CtNewMethod.make(src, nested);
+ nested.addMethod(m);
+ nested.writeFile();
+ outer.writeFile();
+
+ Object iobj = make(nested.getName());
+ assertEquals(6, invoke(iobj, "f"));
+ }
+
+ public void testPrivateMethod() throws Exception {
+ CtClass cc = sloader.get("test2.PrivateMethod");
+ try {
+ CtMethod m = CtNewMethod.make(
+ "public int f(test2.PrivateMethod2 p) { return p.f(); }",
+ cc);
+ fail();
+ }
+ catch (CannotCompileException e) {}
+ }
+
+ public void testArrayLength() throws Exception {
+ CtClass cc = sloader.makeClass("test2.ArrayLength");
+ CtMethod m2 = CtNewMethod.make(
+ "public int f() { String[] s = new String[3]; " +
+ "return s.length; }", cc);
+ cc.addMethod(m2);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "f"));
+ }
+
+ public void testMakeStaticMethod() throws Exception {
+ CtClass cc = sloader.makeClass("test2.MakeStaticMethod");
+ CtMethod m = CtNewMethod.make(Modifier.PUBLIC | Modifier.STATIC,
+ CtClass.intType, "create",
+ new CtClass[] { CtClass.intType }, null,
+ "{ return $1; }", cc);
+ cc.addMethod(m);
+ cc.addMethod(CtNewMethod.make(
+ "public int test() { return create(13); }", cc));
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(13, invoke(obj, "test"));
+ }
+
+ public void testNewExprTry() throws Exception {
+ ExprEditor ed = new ExprEditor() {
+ public void edit(NewExpr expr) throws CannotCompileException {
+ StringBuffer code = new StringBuffer(300);
+ code.append("{ try ");
+ code.append("{ $_ = $proceed($$); }");
+ code.append("catch (OutOfMemoryError e) {}}");
+ expr.replace(code.toString());
+ }
+ };
+
+ CtClass cc = sloader.get("test2.NewExprTry");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ m1.instrument(ed);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(16, invoke(obj, "run"));
+ }
+
+ public void testToClass() throws Exception {
+ ClassPool cp = ClassPool.getDefault();
+ CtClass cc = cp.makeClass("test2.ToClassTest");
+ Class c = cc.toClass(DefineClassCapability.class);
+ assertEquals(getClass().getClassLoader(), c.getClassLoader());
+ }
+
+ public void testAddCatchForConstructor() throws Exception {
+ CtClass cc = sloader.get("test2.AddCatchForConstructor");
+ CtConstructor m1 = cc.getDeclaredConstructors()[0];
+ m1.addCatch("return;", sloader.get("java.lang.Exception"));
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ }
+
+ public void testAddLocalVar() throws Exception {
+ CtClass cc = sloader.get("test2.AddLocalVar");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ m1.addLocalVariable("i", CtClass.intType);
+ m1.insertBefore("i = 3;");
+ m1.insertAfter("$_ = i + 1;");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(4, invoke(obj, "foo"));
+ }
+
+ public void testNewArray() throws Exception {
+ ExprEditor ed = new ExprEditor() {
+ int dim[] = { 1, 2, 2, 1, 2, 2, 3 };
+ int cdim[] = { 1, 1, 2, 1, 1, 2, 2 };
+ int counter = 0;
+ public void edit(NewArray expr) throws CannotCompileException {
+ try {
+ CtClass str = sloader.get("java.lang.String");
+ if (counter < 3)
+ assertEquals(str, expr.getComponentType());
+ else
+ assertEquals(CtClass.intType, expr.getComponentType());
+
+ assertEquals(dim[counter], expr.getDimension());
+ assertEquals(cdim[counter], expr.getCreatedDimensions());
+ expr.replace("{ i += $1; $_ = $proceed($$); }");
+ ++counter;
+ }
+ catch (NotFoundException e) {
+ throw new CannotCompileException(e);
+ }
+ }
+ };
+
+ CtClass cc = sloader.get("test2.NewArray");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ m1.instrument(ed);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(48, invoke(obj, "run"));
+ }
+
+ public void testToString() throws Exception {
+ System.out.println(sloader.get("int"));
+ System.out.println(sloader.get("int[]"));
+ System.out.println(sloader.get("java.lang.Object"));
+ System.out.println(sloader.get("java.lang.String"));
+ System.out.println(sloader.get("javassist.CtNewClass"));
+ }
+
+ public void testDotClass() throws Exception {
+ testDotClass("test2.DotClass", false);
+ testDotClass("test2.DotClass_", true);
+ }
+
+ private void testDotClass(String cname, boolean java5) throws Exception {
+ CtClass cc = sloader.makeClass(cname);
+ if (java5)
+ cc.getClassFile2().setVersionToJava5();
+
+ CtMethod m = CtNewMethod.make(
+ "public String getclass() {" +
+ " return int.class.getName() + int[].class.getName()" +
+ " + String.class.getName() + String[].class.getName()" +
+ " + java.lang.Object.class.getName()" +
+ " + java.util.Vector.class.getName(); }", cc);
+ cc.addMethod(m);
+ CtMethod m2 = CtNewMethod.make(
+ "public int test() {" +
+ " String s = getclass(); System.out.println(s);" +
+ " return s.length(); }", cc);
+ cc.addMethod(m2);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(72, invoke(obj, "test"));
+ }
+
+ public void testDotClass2() throws Exception {
+ testDotClass2("test2.DotClass2", false);
+ testDotClass2("test2.DotClass2_", true);
+ }
+
+ private void testDotClass2(String cname, boolean java5) throws Exception {
+ CtClass cc = sloader.makeClass(cname);
+ CtClass cc3 = sloader.makeClass("test2.DotClass3");
+ if (java5)
+ cc.getClassFile2().setVersionToJava5();
+
+ CtMethod m = CtNewMethod.make(
+ "public int test() {" +
+ " return test2.DotClass3.class.getName().length(); }", cc);
+ cc.addMethod(m);
+ cc.writeFile();
+ // don't execute cc3.writeFile();
+ Object obj = make(cc.getName());
+ try {
+ assertEquals(15, invoke(obj, "test"));
+ }
+ catch (java.lang.reflect.InvocationTargetException e) {
+ Throwable t = e.getCause();
+ assertTrue(t instanceof java.lang.NoClassDefFoundError);
+ }
+ }
+
+ public void testDotClass4() throws Exception {
+ testDotClass4("test2.DotClass4", false);
+ testDotClass4("test2.DotClass4_", true);
+ }
+
+ private void testDotClass4(String cname, boolean java5) throws Exception {
+ CtClass cc = sloader.makeClass(cname);
+ if (java5)
+ cc.getClassFile2().setVersionToJava5();
+
+ CtMethod m = CtNewMethod.make(
+ "public int test() {" +
+ " String s = Object.class.getName()" +
+ " + Object[].class.getName();" +
+ " return s.length(); }", cc);
+ cc.addMethod(m);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(35, invoke(obj, "test"));
+ }
+
+ public void testSuperInterface() throws Exception {
+ CtClass cc = sloader.makeClass("test2.SuperInterface3");
+ CtClass cc2 = sloader.get("test2.SuperInterface2");
+ cc.addInterface(cc2);
+ cc.addField(new CtField(cc2, "inner", cc));
+ CtMethod m = CtNewMethod.make(
+ "public int getAge() { return inner.getAge(); }", cc);
+ cc.addMethod(m);
+ cc.writeFile();
+ }
+
+ public void testPrune() throws Exception {
+ CtClass cc = sloader.get("test2.Prune");
+ cc.stopPruning(false);
+ System.out.println(cc);
+ cc.addField(new CtField(CtClass.intType, "f", cc));
+ cc.toBytecode();
+ try {
+ cc.defrost();
+ fail("can call defrost()");
+ }
+ catch (RuntimeException e) {
+ assertTrue(e.getMessage().indexOf("prune") >= 0);
+ }
+
+ System.out.println(cc);
+ }
+
+ public void testNewExprInTry() throws Exception {
+ ExprEditor ed = new ExprEditor() {
+ public void edit(NewExpr expr) throws CannotCompileException {
+ expr.replace("$_ = new test2.HashMapWrapper($1, 1);");
+ }
+ };
+
+ CtClass cc = sloader.get("test2.NewExprInTry");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ m1.instrument(ed);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "run"));
+ }
+
+ public void testConstField() throws Exception {
+ CtClass cc = sloader.get("test2.ConstField");
+ CtField f;
+ f = cc.getField("b");
+ assertEquals(true, ((Boolean)f.getConstantValue()).booleanValue());
+ f = cc.getField("i");
+ assertEquals(3, ((Integer)f.getConstantValue()).intValue());
+ f = cc.getField("j");
+ assertEquals(7L, ((Long)f.getConstantValue()).longValue());
+ f = cc.getField("f");
+ assertEquals(8.0F, ((Float)f.getConstantValue()).floatValue(), 0.0);
+ f = cc.getField("d");
+ assertEquals(9.0, ((Double)f.getConstantValue()).doubleValue(), 0.0);
+ f = cc.getField("s");
+ assertEquals("const", f.getConstantValue());
+ f = cc.getField("obj");
+ assertEquals(null, f.getConstantValue());
+ f = cc.getField("integer");
+ assertEquals(null, f.getConstantValue());
+ f = cc.getField("k");
+ assertEquals(null, f.getConstantValue());
+
+ cc.getClassFile().prune();
+
+ f = cc.getField("i");
+ assertEquals(3, ((Integer)f.getConstantValue()).intValue());
+ f = cc.getField("k");
+ assertEquals(null, f.getConstantValue());
+ }
+
+ public void testWhere() throws Exception {
+ CtClass cc = sloader.get("test2.Where");
+ CtConstructor cons = cc.getClassInitializer();
+ cons.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ System.out.println(m.where().getName());
+ }
+ });
+
+ }
+
+ public void testNewOp() throws Exception {
+ CtClass cc = sloader.get("test2.NewOp");
+
+ CtMethod add = CtNewMethod.make(
+"public test2.NewOp2 " +
+" addMonitoringRemoteEventListener(" +
+" test2.NewOp2 listener)" +
+" throws java.rmi.RemoteException {" +
+" $0.listenerList.addElement(listener);" +
+" return new test2.NewOp2(0L, this, null, 0L);" +
+"}", cc);
+
+ add.setModifiers(Modifier.PUBLIC);
+ cc.addMethod(add);
+ /*
+ CtMethod type= CtNewMethod.make(
+ "public test2.Where getNewType() { return new test2.Where(); }",
+ cc);
+ cc.addMethod(type);
+ */
+ cc.writeFile();
+ }
+
+ public void testSwitch() throws Exception {
+ CtClass cc = sloader.makeClass("test2.Switch");
+
+ cc.addMethod(CtNewMethod.make(
+ "public int test1() {" +
+ " int i = 1;" +
+ " int j;" +
+ " switch (i) {" +
+ " case 0: j = i; break;" +
+ " case 1: j = -i; break;" +
+ " default: j = 0; break;" +
+ " }" +
+ " return j; }", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int test2() {" +
+ " int i = 2;" +
+ " int j = 7;" +
+ " switch (i) {" +
+ " case 0: j = i; break;" +
+ " case 1: j = -i; break;" +
+ " }" +
+ " return j; }", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int test3() {" +
+ " int i = Byte.MAX_VALUE;" +
+ " int j;" +
+ " switch (i) {" +
+ " case Byte.MAX_VALUE: j = i; break;" +
+ " case Byte.MIN_VALUE: j = -i; break;" +
+ " default: j = 0; break;" +
+ " }" +
+ " return j; }", cc));
+
+ try {
+ cc.addMethod(CtNewMethod.make(
+ "public int test4() {" +
+ " int i = Byte.MAX_VALUE;" +
+ " int j;" +
+ " switch (i) {" +
+ " case Byte.MAX_VALUE: j = i; return j;" +
+ " case Byte.MIN_VALUE: j = -i; return j;" +
+ " default: j = 0;" +
+ " }" +
+ "}", cc));
+ fail("does not report an error (no return)");
+ }
+ catch (CannotCompileException e) { System.out.println(e); }
+
+ try {
+ cc.addMethod(CtNewMethod.make(
+ "public int test5() {" +
+ " int i = Byte.MAX_VALUE;" +
+ " int j;" +
+ " switch (i) {" +
+ " case Byte.MAX_VALUE: j = i; return j;" +
+ " case Byte.MIN_VALUE: j = -i; return j;" +
+ " }" +
+ "}", cc));
+ fail("does not report an error (not default)");
+ }
+ catch (CannotCompileException e) { System.out.println(e); }
+
+ try {
+ cc.addMethod(CtNewMethod.make(
+ "public int test6() {" +
+ " int i = Byte.MAX_VALUE;" +
+ " int j;" +
+ " switch (i) {" +
+ " case Byte.MAX_VALUE: j = i; break;" +
+ " default: j = -i; return j;" +
+ " }" +
+ " }", cc));
+ fail("does not report an error (break)");
+ }
+ catch (CannotCompileException e) { System.out.println(e); }
+
+ cc.addField(CtField.make("public static int k;", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public void foo() {" +
+ " int i = 0;" +
+ " k = 3;" +
+ " switch (i) {" +
+ " case Byte.MAX_VALUE: k = 1;" +
+ " case Byte.MIN_VALUE: k = 2;" +
+ " }" +
+ "}", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int test7() {" +
+ " int i = Byte.MAX_VALUE;" +
+ " int j = 3; foo();" +
+ " System.out.println(k);" +
+ " switch (i) {" +
+ " case Byte.MAX_VALUE: return k;" +
+ " case Byte.MIN_VALUE: return j;" +
+ " default: return 0;" +
+ " }" +
+ "}", cc));
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(-1, invoke(obj, "test1"));
+ assertEquals(7, invoke(obj, "test2"));
+ assertEquals(Byte.MAX_VALUE, invoke(obj, "test3"));
+ assertEquals(3, invoke(obj, "test7"));
+ }
+
+ public void testGet() throws Exception {
+ CtClass cc = sloader.get("char[]");
+ CtClass cc2 = cc.getComponentType();
+ System.out.println(cc2);
+ }
+
+ public void testSynchronized() throws Exception {
+ CtClass cc = sloader.makeClass("test2.Synch");
+
+ cc.addMethod(CtNewMethod.make(
+ "public synchronized int test1() {" +
+ " int i = 0;" +
+ " synchronized (this) {" +
+ " i = 3;" +
+ " }" +
+ " return i; }", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public synchronized int test2() {" +
+ " int i = 0;" +
+ " synchronized (this) {" +
+ " i = 3;" +
+ " return i;" +
+ " }" +
+ "}", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public synchronized int test3() {" +
+ " int i = 0;" +
+ " synchronized (this) {" +
+ " if (this instanceof String)" +
+ " return i;" +
+ " else" +
+ " i = 3;" +
+ " }" +
+ " return i;" +
+ "}", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public synchronized int test4() {" +
+ " int i = 0;" +
+ " synchronized (this) {" +
+ " }" +
+ " return i; }", cc));
+
+ try {
+ cc.addMethod(CtNewMethod.make(
+ "public synchronized int test5() {" +
+ " while (true)" +
+ " synchronized (this) {" +
+ " break;" +
+ " }" +
+ " return i; }", cc));
+ fail("does not report an error");
+ }
+ catch (CannotCompileException e) { System.out.println(e); }
+
+ cc.addMethod(CtNewMethod.make(
+ "public synchronized int test6() {" +
+ " int i = 0;" +
+ " while (true) {" +
+ " synchronized (this) {" +
+ " i = 3;" +
+ " }" +
+ " break; }" +
+ " return i; }", cc));
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "test1"));
+ assertEquals(3, invoke(obj, "test2"));
+ assertEquals(3, invoke(obj, "test3"));
+ assertEquals(0, invoke(obj, "test4"));
+ }
+
+ public void testTryFinally() throws Exception {
+ CtClass cc = sloader.get("test2.Finally");
+
+ cc.addMethod(CtNewMethod.make(
+ "public int test1() {" +
+ " a = 0;" +
+ " try {" +
+ " update();" +
+ " } catch (NullPointerException e) {" +
+ " a = 1;" +
+ " } finally {" +
+ " a = 2;" +
+ " }" +
+ " return a; }", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int test2() {" +
+ " a = 0;" +
+ " try {" +
+ " update(); return a;" +
+ " } catch (NullPointerException e) {" +
+ " a = 1; throw e;" +
+ " } finally {" +
+ " a = 2; return a;" +
+ " }" +
+ "}", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int test3() {" +
+ " a = 0;" +
+ " try {" +
+ " update(); return a;" +
+ " } catch (NullPointerException e) {" +
+ " a = 1;" +
+ " } finally {" +
+ " a = 2;" +
+ " }" +
+ " return a;" +
+ "}", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int test4() {" +
+ " a = 0;" +
+ " try {" +
+ " update(); return a;" +
+ " } catch (NullPointerException e) {" +
+ " a = 1; return a;" +
+ " } finally {" +
+ " a = 2;" +
+ " }" +
+ "}", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public double test5() {" +
+ " b = 1.0;" +
+ " try {" +
+ " return b;" +
+ // " } catch (NullPointerException e) {" +
+ // " b = 2.0; return b;" +
+ " } finally {" +
+ " b += 3.0;" +
+ " }" +
+ "}", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int test5a() {" +
+ " return (int)test5();" +
+ "}", cc));
+
+ cc.addMethod(CtNewMethod.make(
+ "public int test6() {" +
+ " a = 0;" +
+ " try {" +
+ " if (a > 0)" +
+ " return a;" +
+ " update(); a = 1;" +
+ " }" +
+ " catch (RuntimeException e) {" +
+ " if (a > 0) a = 2; else a = 3;" +
+ " }" +
+ " catch (Throwable e) {" +
+ " a = 4;" +
+ " } finally {" +
+ " try { if (a < 0) update(); return a; }" +
+ " finally { if (a > 0) return a; a = 5; }" +
+ " " +
+ " }" +
+ "}", cc));
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(2, invoke(obj, "test1"));
+ assertEquals(2, invoke(obj, "test2"));
+ assertEquals(2, invoke(obj, "test3"));
+ assertEquals(1, invoke(obj, "test4"));
+ assertEquals(1, invoke(obj, "test5a"));
+ assertEquals(3, invoke(obj, "test6"));
+ }
+
+ public void testConstructorName() throws Exception {
+ CtClass cc = sloader.get("test2.Construct");
+ CtConstructor[] cons = cc.getDeclaredConstructors();
+ assertEquals("Construct", cons[0].getName());
+ assertEquals("Construct", cons[1].getName());
+ assertEquals("<clinit>", cc.getClassInitializer().getName());
+ }
+
+ public void testRemoveCall() throws Exception {
+ CtClass cc = sloader.get("test2.RemoveCall");
+ CtMethod m1 = cc.getDeclaredMethod("bar");
+ m1.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ m.replace("{ $_ = ($r)null; }");
+ }
+ });
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(0, invoke(obj, "bar"));
+ }
+
+ public void testRemove() throws Exception {
+ CtClass cc = sloader.get("test2.Remove");
+ testRemove2(cc, "f1");
+ testRemove2(cc, "f6");
+ testRemove2(cc, "f3");
+ CtField p = cc.getField("p");
+ try {
+ cc.removeField(p);
+ fail("non-existing field has been removed");
+ }
+ catch (NotFoundException e) {}
+
+ testRemove3(cc, "bar");
+ testRemove3(cc, "bar2");
+ testRemove4(cc, "(I)V");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(7, invoke(obj, "foo"));
+ }
+
+ private void testRemove2(CtClass cc, String fieldName) throws Exception {
+ CtField f = cc.getField(fieldName);
+ cc.removeField(f);
+ try {
+ CtField f2 = cc.getField(fieldName);
+ fail("the removed field still exists");
+ }
+ catch (NotFoundException e) {}
+ }
+
+ private void testRemove3(CtClass cc, String methodName) throws Exception {
+ CtMethod m = cc.getDeclaredMethod(methodName);
+ cc.removeMethod(m);
+ try {
+ CtMethod m2 = cc.getDeclaredMethod(methodName);
+ fail("the removed method still exists");
+ }
+ catch (NotFoundException e) {}
+ }
+
+ private void testRemove4(CtClass cc, String desc) throws Exception {
+ CtConstructor c = cc.getConstructor(desc);
+ cc.removeConstructor(c);
+ try {
+ CtConstructor c2 = cc.getConstructor(desc);
+ fail("the removed method still exists");
+ }
+ catch (NotFoundException e) {}
+ }
+
+ public void testGetAndRename() throws Exception {
+ try {
+ CtClass cc = sloader.getAndRename("NotExisting", "Existing");
+ }
+ catch (NotFoundException e) {
+ System.out.println(e);
+ }
+ }
+
+ public void testConstBody() throws Exception {
+ CtClass cc = sloader.get("test2.ConstBody");
+ CtConstructor cons = new CtConstructor(new CtClass[] {
+ sloader.get("java.lang.String"),
+ sloader.get("java.lang.Integer") }, cc);
+ cons.setBody("super((String)$1, (Integer)$2);");
+ cc.addConstructor(cons);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "bar"));
+ }
+
+ private String methodCallData = null;
+
+ public void testMethodCall() throws Exception {
+ CtClass cc = sloader.get("test2.MethodCall");
+
+ CtMethod m1 = cc.getDeclaredMethod("bar");
+ m1.instrument(new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ if ("clone".equals(m.getMethodName()))
+ methodCallData = m.getClassName();
+ }
+ });
+
+ cc.writeFile();
+ assertEquals("java.lang.String[]", methodCallData);
+
+ assertEquals("java.lang.String[]",
+ sloader.get("[Ljava/lang/String;").getName());
+ assertEquals("int[][]",
+ sloader.get("[[I").getName());
+ }
+
+ public void testKmatcha() throws Exception {
+ CtClass cc = sloader.makeClass("test2.Kmatcha");
+ cc.addMethod(CtNewMethod.make(
+"public void display(String [] params){" +
+" if(params == null){" +
+" System.out.println(\"Nothing to display\");" +
+" }else{" +
+" int k = params.length - 1;" +
+" if(k >= 0)" +
+" do " +
+" System.out.println(params[k]);" +
+" while(--k >= 0);" +
+" }}", cc));
+ }
+
+ public void testStrict() throws Exception {
+ CtClass cc = sloader.makeClass("test2.StrictTest");
+ cc.addMethod(CtNewMethod.make(
+"public strictfp int foo(){ " +
+" int strict = 1; return strict + 1; }", cc));
+ }
+
+ public void testArrayLen() throws Exception {
+ CtClass cc = sloader.get("test2.ArrayLenTest");
+ cc.addMethod(CtNewMethod.make(
+"public int foo(){ return this.length; }", cc));
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "foo"));
+ }
+
+ public void testUnicodeIdentifier() throws Exception {
+ CtClass cc = sloader.makeClass("test2.UnicodeIdentifier");
+ String src = "public int foo(){ int \u5206 = 0; return \u5206; }";
+ cc.addMethod(CtNewMethod.make(src, cc));
+ }
+
+ public void testBrennan() throws Exception {
+ CtClass cc = sloader.get("test2.Brennan");
+ cc.addMethod(CtNewMethod.make(
+"public int foo(){" +
+" java.text.SimpleDateFormat df;" +
+" if((df = (java.text.SimpleDateFormat)format) == null)" +
+" df = new java.text.SimpleDateFormat(\"yyyyMMdd\");" +
+" return 1;}", cc));
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "foo"));
+ }
+
+ public void testArrayAndNull() throws Exception {
+ CtClass cc = sloader.get("test2.ArrayAndNull");
+ CtMethod m = cc.getDeclaredMethod("test");
+ m.insertAfter("if ($_ == null) $_ = new int[0];");
+ }
+
+ public void testStaticArrays() throws Exception {
+ CtClass cc = sloader.makeClass("StaticArrays");
+ CtField f = new CtField(sloader.get("test2.StaticArraysMem[]"),
+ "myStaticField", cc);
+
+ f.setModifiers(Modifier.STATIC);
+ cc.addField(f);
+ CtConstructor init = cc.makeClassInitializer();
+ String body = "{\n";
+ body += ("myStaticField = new test2.StaticArraysMem[2];\n");
+ body += ("\n}");
+ init.setBody(body);
+ }
+
+ public void testObjectSuper() throws Exception {
+ CtClass cc = sloader.get("java.lang.Object");
+ try {
+ cc.addMethod(CtNewMethod.make(
+ "public int foo(){ return super.hashCode(); }", cc));
+ fail("could access the super of java.lang.Object");
+ }
+ catch (CannotCompileException e) {}
+ }
+
+ public void testStaticFinal() throws Exception {
+ CtClass cc = sloader.makeClass("test2.StaticFinal");
+ CtField f = new CtField(CtClass.intType, "sff1", cc);
+ f.setModifiers(Modifier.STATIC | Modifier.FINAL);
+ cc.addField(f, "5");
+ assertEquals(Integer.valueOf(5), f.getConstantValue());
+
+ f = new CtField(CtClass.longType, "sff2", cc);
+ f.setModifiers(Modifier.STATIC | Modifier.FINAL);
+ cc.addField(f, "6");
+ assertEquals(Long.valueOf(6), f.getConstantValue());
+
+ f = new CtField(CtClass.floatType, "sff3", cc);
+ f.setModifiers(Modifier.STATIC | Modifier.FINAL);
+ cc.addField(f, "7");
+ assertEquals(Float.valueOf(7.0F), f.getConstantValue());
+
+ f = new CtField(CtClass.floatType, "sff4", cc);
+ f.setModifiers(Modifier.STATIC | Modifier.FINAL);
+ cc.addField(f, "8.0");
+ assertEquals(Float.valueOf(8.0F), f.getConstantValue());
+
+ f = new CtField(CtClass.doubleType, "sff5", cc);
+ f.setModifiers(Modifier.STATIC | Modifier.FINAL);
+ cc.addField(f, "9");
+ assertEquals(Double.valueOf(9.0), f.getConstantValue());
+
+ f = new CtField(CtClass.doubleType, "sff6", cc);
+ f.setModifiers(Modifier.STATIC | Modifier.FINAL);
+ cc.addField(f, "10.0");
+ assertEquals(Double.valueOf(10.0), f.getConstantValue());
+
+ f = new CtField(sloader.get("java.lang.String"), "sff7", cc);
+ f.setModifiers(Modifier.STATIC | Modifier.FINAL);
+ cc.addField(f, "\"test\"");
+ assertEquals("test", f.getConstantValue());
+
+ f = new CtField(sloader.get("java.lang.String"), "sff8", cc);
+ f.setModifiers(Modifier.STATIC);
+ cc.addField(f, "\"static\"");
+ assertEquals(null, f.getConstantValue());
+
+ cc.addMethod(CtNewMethod.make(
+ "public int foo(){ return sff1 + sff7.length(); }", cc));
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(9, invoke(obj, "foo"));
+ }
+
+ public void testLocalVar() throws Exception {
+ CtClass cc = sloader.get("test2.LocalVar");
+ CtMethod m = cc.getDeclaredMethod("toString");
+ m.addLocalVariable("var", CtClass.booleanType);
+ m.insertBefore("{var = true; }");
+ m.insertAfter("{if (var) hashCode(); }", false);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "foo"));
+ }
+
+ public void testImportPackage() throws Exception {
+ CtClass cc2 = sloader.makeClass("test2.Imported");
+ cc2.writeFile();
+ CtClass cc = sloader.makeClass("test2.Importer");
+ sloader.importPackage("test2");
+ cc.addMethod(CtNewMethod.make(
+ "public int foo(){ " +
+ " Imported obj = new Imported();" +
+ " return obj.getClass().getName().length(); }", cc));
+ sloader.clearImportedPackages();
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(14, invoke(obj, "foo"));
+ }
+
+ public void testArrayInit() throws Exception {
+ CtClass cc = sloader.makeClass("test2.ArrayInit");
+ cc.addMethod(CtNewMethod.make(
+ "public int foo(){ " +
+ " int[] i = new int[] { 1, 2 };" +
+ " double[] d = new double[] { 3.0, 4.0 };" +
+ " String[] s = new String[] { \"foo\", \"12345\" };" +
+ " return i[0] + (int)d[0] + s[1].length(); }", cc));
+ cc.addMethod(CtNewMethod.make(
+ "public int bar(){ " +
+ " int[] i = { 1, 2.0 };" +
+ " double[] d = { 3.0, 4 };" +
+ " String[] s = { \"foo\", \"12345\" };" +
+ " return i[0] + (int)d[0] + s[1].length(); }", cc));
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(9, invoke(obj, "foo"));
+ assertEquals(9, invoke(obj, "bar"));
+ }
+}
diff --git a/src/test/javassist/JvstTest3.java b/src/test/javassist/JvstTest3.java
new file mode 100644
index 0000000..46f06b1
--- /dev/null
+++ b/src/test/javassist/JvstTest3.java
@@ -0,0 +1,1127 @@
+package javassist;
+
+import javassist.bytecode.*;
+import javassist.bytecode.annotation.*;
+import javassist.expr.*;
+import test3.*;
+
+@SuppressWarnings({"rawtypes","unchecked","unused"})
+public class JvstTest3 extends JvstTestRoot {
+ public JvstTest3(String name) {
+ super(name);
+ }
+
+ public void testAnnotation() throws Exception {
+ CtClass cc = sloader.get("test3.AnnoTest");
+ Object[] all = cc.getAnnotations();
+ Anno a = (Anno)all[0];
+ assertEquals('0', a.c());
+ assertEquals(true, a.bool());
+ assertEquals(1, a.b());
+ assertEquals(2, a.s());
+ assertEquals(3, a.i());
+ assertEquals(4L, a.j());
+ assertEquals(5.0F, a.f());
+ assertEquals(6.0, a.d());
+ assertEquals("7", a.str());
+ assertEquals(AnnoTest.class, a.clazz());
+ assertEquals(3, a.anno2().str().length);
+ }
+
+ public void testAnnotation2() throws Exception {
+ CtClass cc = sloader.get("test3.AnnoTest2");
+ Object[] all = cc.getAnnotations();
+ Anno a = (Anno)all[0];
+ assertEquals('a', a.c());
+ assertEquals(false, a.bool());
+ assertEquals(11, a.b());
+ assertEquals(12, a.s());
+ assertEquals(13, a.i());
+ assertEquals(14L, a.j());
+ assertEquals(15.0F, a.f());
+ assertEquals(16.0, a.d());
+ assertEquals("17", a.str());
+ assertEquals(String.class, a.clazz());
+ assertEquals(11, a.anno2().i()[0]);
+ }
+
+ public void testAnnotation3() throws Exception {
+ CtClass cc = sloader.get("test3.AnnoTest3");
+ Object[] all = cc.getAnnotations();
+ assertEquals(2, all.length);
+ int i;
+ if (all[0] instanceof Anno2)
+ i = 0;
+ else
+ i = 1;
+
+ Anno2 a = (Anno2)all[i];
+ assertEquals(1, a.i()[0]);
+ assertEquals(test3.ColorName.RED, a.color());
+ assertEquals(test3.ColorName.BLUE, a.color2()[0]);
+ }
+
+ public void testAnnotation4() throws Exception {
+ CtClass cc = sloader.get("test3.AnnoTest4");
+ Object[] all = cc.getAnnotations();
+ Anno3 a = null;
+ for (int i = 0; i < all.length; i++)
+ if (all[i] instanceof Anno3)
+ a = (Anno3)all[i];
+
+ assertTrue(a != null);
+ assertEquals('0', a.c()[0]);
+ assertEquals(true, a.bool()[0]);
+ assertEquals(1, a.b()[0]);
+ assertEquals(2, a.s()[0]);
+ assertEquals(3, a.i()[0]);
+ assertEquals(4L, a.j()[0]);
+ assertEquals(5.0F, a.f()[0]);
+ assertEquals(6.0, a.d()[0]);
+ assertEquals("7", a.str()[0]);
+ assertEquals(AnnoTest.class, a.clazz()[0]);
+ assertEquals(11, a.anno2()[0].i()[0]);
+ }
+
+ public void testAnnotation5() throws Exception {
+ CtClass cc = sloader.get("test3.AnnoTest5");
+ Object[] all = cc.getField("bar").getAnnotations();
+ Anno2 a2 = (Anno2)all[0];
+ assertEquals(test3.ColorName.RED, a2.color());
+
+ all = cc.getDeclaredMethod("foo").getAnnotations();
+ Anno a = (Anno)all[0];
+ assertEquals("7", a.str());
+ }
+
+ public void testAnnotation6() throws Exception {
+ CtClass cc = sloader.get("test3.AnnoTest6");
+ Object[] all = cc.getAnnotations();
+ Anno6 a = (Anno6)all[0];
+ assertEquals(0, a.str1().length);
+ assertEquals(0, a.str2().length);
+ }
+
+ public void testChainedException() throws Exception {
+ try {
+ throwChainedException();
+ }
+ catch (CannotCompileException e) {
+ e.printStackTrace(System.out);
+ }
+
+ try {
+ throwChainedException2();
+ }
+ catch (CannotCompileException e) {
+ e.printStackTrace(System.out);
+ }
+
+ try {
+ throwChainedException3();
+ }
+ catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+
+ }
+
+ public void throwChainedException() throws Exception {
+ throw new CannotCompileException("test");
+ }
+
+ public void throwChainedException2() throws Exception {
+ Throwable e = new CannotCompileException("test");
+ throw new CannotCompileException("test2", e);
+ }
+
+ public void throwChainedException3() throws Exception {
+ Throwable e = new CannotCompileException("testA");
+ Throwable e2 = new CannotCompileException("testB", e);
+ throw new Exception(e2);
+ }
+
+ // JIRA Javassist-12
+ public void testInnerClassMethod() throws Exception {
+ CtClass cc = sloader.get("test3.InnerMethod");
+ CtMethod m1 = cc.getDeclaredMethod("test");
+ m1.setBody("{inner.test();}");
+
+ CtMethod m2 = CtNewMethod.make(
+ "public int bar() {"
+ + " if (counter-- <= 0) return 3;"
+ + " else return bar();"
+ + "}",
+ cc);
+ cc.addMethod(m2);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "foo"));
+ assertEquals(3, invoke(obj, "bar"));
+ }
+
+ public void testCheckModifyAndPruned() throws Exception {
+ CtClass cc = sloader.get("test3.CheckModify");
+ cc.addField(new CtField(CtClass.intType, "j", cc));
+ cc.stopPruning(false);
+ cc.toBytecode();
+ try {
+ cc.getClassFile();
+ fail();
+ }
+ catch (RuntimeException e) {
+ // System.err.println(e.getMessage());
+ assertTrue(e.getMessage().indexOf("prune") >= 0);
+ }
+ }
+
+ public void testReplaceNew() throws Exception {
+ CtClass cc = sloader.get("test3.ReplaceNew");
+ CtMethod m1 = cc.getDeclaredMethod("run");
+ m1.instrument(new ExprEditor() {
+ public void edit(NewExpr n) throws CannotCompileException {
+ n.replace("{ i++; $_ = null; }");
+ }
+ });
+
+ CtMethod m2 = cc.getDeclaredMethod("run2");
+ m2.instrument(new ExprEditor() {
+ public void edit(NewExpr n) throws CannotCompileException {
+ n.replace("{ j++; $_ = null; }");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(5, invoke(obj, "run"));
+ assertEquals(2, invoke(obj, "run2"));
+ }
+
+ public void testPublicInner() throws Exception {
+ CtClass cc0 = sloader.get("test3.PublicInner2");
+ int mod = cc0.getClassFile2().getAccessFlags();
+ System.out.println("testPublicInner: " + mod);
+
+ CtClass cc = sloader.get("test3.PublicInner");
+ CtClass jp = cc.makeNestedClass("Inner", true);
+ assertEquals(Modifier.PUBLIC | Modifier.STATIC, jp.getModifiers());
+ assertEquals(Modifier.PUBLIC | Modifier.STATIC,
+ getPublicInner(jp, "Inner"));
+ assertEquals(Modifier.PUBLIC | Modifier.STATIC,
+ getPublicInner(cc, "Inner"));
+
+ jp.setModifiers(Modifier.STATIC);
+ assertEquals(Modifier.STATIC, jp.getModifiers());
+ assertEquals(Modifier.STATIC, getPublicInner(jp, "Inner"));
+ assertEquals(Modifier.STATIC, getPublicInner(cc, "Inner"));
+
+ jp.setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT);
+ assertEquals(Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.STATIC, jp.getModifiers());
+ assertEquals(Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.STATIC,
+ getPublicInner(jp, "Inner"));
+ assertEquals(Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.STATIC,
+ getPublicInner(cc, "Inner"));
+
+ cc.writeFile();
+ jp.writeFile();
+ }
+
+ private int getPublicInner(CtClass cc, String name) throws Exception {
+ ClassFile cf = cc.getClassFile();
+ InnerClassesAttribute ica
+ = (InnerClassesAttribute)cf.getAttribute(
+ InnerClassesAttribute.tag);
+ assertEquals(name, ica.innerName(0));
+ return ica.accessFlags(0);
+ }
+
+ public void testConstructorToMethod() throws Exception {
+ CtClass cc = sloader.get("test3.Constructor");
+ CtConstructor[] cons = cc.getConstructors();
+ CtConstructor sinit = cc.getClassInitializer();
+
+ for (int i = 0; i < cons.length; i++) {
+ CtConstructor ccons = cons[i];
+ String desc = ccons.getSignature();
+ boolean result = false;
+ if (desc.equals("()V"))
+ result = false;
+ else if (desc.equals("(I)V"))
+ result = true;
+ else if (desc.equals("(Ljava/lang/String;)V"))
+ result = false;
+ else if (desc.equals("(D)V"))
+ result = true;
+ else
+ fail("unknonw constructor");
+
+ assertEquals(result, ccons.callsSuper());
+ }
+
+ CtClass cc2 = sloader.get("test3.Constructor2");
+ for (int i = 0; i < cons.length; i++)
+ cc2.addMethod(cons[i].toMethod("m", cc2));
+
+ cc2.addMethod(sinit.toMethod("sinit", cc2));
+ cc2.addMethod(CtMethod.make(
+ "public int run() { m(); m(5); m(\"s\"); m(0.0);" +
+ " sinit(); return i + str.length(); }",
+ cc2));
+ cc2.writeFile();
+
+ Object obj = make(cc2.getName());
+ assertEquals(119, invoke(obj, "run"));
+ }
+
+ public void testUnique() throws Exception {
+ CtClass cc = sloader.get("test3.Unique");
+ CtClass cc2 = sloader.get("test3.Unique3");
+ assertEquals("poi", cc.makeUniqueName("poi"));
+ assertEquals("foo102", cc.makeUniqueName("foo"));
+ assertEquals("bar102", cc2.makeUniqueName("bar"));
+ assertEquals("foo100", cc2.makeUniqueName("foo"));
+ }
+
+ public void testGetMethods() throws Exception {
+ CtClass cc = sloader.get("test3.GetMethods");
+ assertEquals(3, cc.getConstructors().length);
+ assertEquals(6, cc.getFields().length);
+ assertEquals(6 + Object.class.getMethods().length + 2,
+ cc.getMethods().length);
+ }
+
+ public void testVisiblity() throws Exception {
+ CtClass cc = sloader.get("test3.Visible");
+ CtClass cc2 = sloader.get("test3.Visible2");
+ CtClass subcc = sloader.get("test3.sub.Visible");
+ CtClass subcc2 = sloader.get("test3.sub.Visible2");
+ CtClass top = sloader.get("VisibleTop");
+ CtClass top2 = sloader.get("VisibleTop2");
+
+ assertEquals(true, cc.getField("pub").visibleFrom(cc2));
+ assertEquals(true, cc.getField("pub").visibleFrom(subcc));
+
+ assertEquals(true, cc.getField("pri").visibleFrom(cc));
+ assertEquals(false, cc.getField("pri").visibleFrom(cc2));
+
+ assertEquals(true, cc.getField("pack").visibleFrom(cc));
+ assertEquals(true, cc.getField("pack").visibleFrom(cc2));
+ assertEquals(false, cc.getField("pack").visibleFrom(subcc));
+ assertEquals(false, cc.getField("pack").visibleFrom(top));
+
+ assertEquals(true, cc.getField("pro").visibleFrom(cc));
+ assertEquals(true, cc.getField("pro").visibleFrom(cc2));
+ assertEquals(true, cc.getField("pro").visibleFrom(subcc2));
+ assertEquals(false, cc.getField("pro").visibleFrom(subcc));
+ assertEquals(false, cc.getField("pro").visibleFrom(top));
+
+ assertEquals(true, top.getField("pack").visibleFrom(top2));
+ assertEquals(false, top.getField("pack").visibleFrom(cc));
+ }
+
+ public void testNewAnnotation() throws Exception {
+ CtClass c = sloader.makeClass("test3.NewClass");
+ ClassFile cf = c.getClassFile();
+ ConstPool cp = cf.getConstPool();
+ AnnotationsAttribute attr
+ = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
+ javassist.bytecode.annotation.Annotation a
+ = new Annotation("test3.ChibaAnnotation", cp);
+ a.addMemberValue("name", new StringMemberValue("Chiba", cp));
+ a.addMemberValue("version", new StringMemberValue("Chiba", cp));
+ a.addMemberValue("description", new StringMemberValue("Chiba", cp));
+ a.addMemberValue("interfaceName", new StringMemberValue("Chiba", cp));
+ attr.setAnnotation(a);
+ System.out.println(attr);
+ cf.addAttribute(attr);
+ cf.setVersionToJava5();
+ Object [] ans = c.getAnnotations() ;
+ System.out.println("Num Annotation : " +ans.length);
+
+ // c.debugWriteFile();
+ Class newclass = c.toClass(DefineClassCapability.class);
+ java.lang.annotation.Annotation[] anns = newclass.getAnnotations();
+ System.out.println("Num NewClass Annotation : " +anns.length);
+ assertEquals(ans.length, anns.length);
+ }
+
+ public void testRecursiveReplace() throws Exception {
+ CtClass cc = sloader.get("test3.RecReplace");
+ CtMethod m1 = cc.getDeclaredMethod("run");
+ final ExprEditor e2 = new ExprEditor() {
+ public void edit(MethodCall mc) throws CannotCompileException {
+ if (mc.getMethodName().equals("bar")) {
+ mc.replace("{ double k = 10.0; $_ = $proceed($1 + k); }");
+ }
+ }
+ };
+ m1.instrument(new ExprEditor() {
+ public void edit(MethodCall mc) throws CannotCompileException {
+ if (mc.getMethodName().equals("foo")) {
+ mc.replace("{ int k = bar($$); $_ = k + $proceed(7.0); }",
+ e2);
+ }
+ }
+ });
+
+ CtMethod m2 = cc.getDeclaredMethod("run2");
+ m2.instrument(new ExprEditor() {
+ public void edit(MethodCall mc) throws CannotCompileException {
+ if (mc.getMethodName().equals("foo")) {
+ mc.replace("{ int k = bar($$); $_ = k + $proceed(7.0); }");
+ }
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(26, invoke(obj, "run"));
+ assertEquals(16, invoke(obj, "run2"));
+ }
+
+ public void testRecursiveReplace2() throws Exception {
+ final ExprEditor[] ref = new ExprEditor[1];
+ ExprEditor e2 = new ExprEditor() {
+ public void edit(FieldAccess fa) throws CannotCompileException {
+ if (fa.getFieldName().equals("value2")
+ && fa.isWriter()) {
+ fa.replace("{ $_ = $proceed($1 + 2); }");
+ }
+ }
+ };
+ ExprEditor e1 = new ExprEditor() {
+ public void edit(FieldAccess fa) throws CannotCompileException {
+ if (fa.getFieldName().equals("value")
+ && fa.isWriter()) {
+ fa.replace("{ value2 = $1; value = value2; }",
+ ref[0]);
+ }
+ }
+ };
+
+ CtClass cc = sloader.get("test3.RecReplace2");
+ CtMethod m1 = cc.getDeclaredMethod("run");
+ ref[0] = e2;
+ m1.instrument(e1);
+ CtMethod m2 = cc.getDeclaredMethod("run2");
+ ref[0] = null;
+ m2.instrument(e1);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(28, invoke(obj, "run"));
+ assertEquals(24, invoke(obj, "run2"));
+ }
+
+ public void testInnerModifier() throws Exception {
+ CtClass cc = sloader.get("test3.InnerClass$Inner");
+ assertEquals(Modifier.PUBLIC | Modifier.STATIC, cc.getModifiers());
+ CtClass cc2 = sloader.get("test3.InnerClass$Inner2");
+ assertEquals(Modifier.PUBLIC, cc2.getModifiers());
+ }
+
+ public void testMethodLookup() throws Exception {
+ CtClass cc = sloader.get("test3.SubValue");
+ CtMethod m1 = CtNewMethod.make(
+ "public int run() {" +
+ " test3.SuperValue sup = new test3.SuperValue();" +
+ " test3.SubValue sub = new test3.SubValue();" +
+ " return this.after(sup, sub, sub) == null ? 0 : 1;" +
+ "}",
+ cc);
+ cc.addMethod(m1);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "run"));
+ }
+
+ public void testFieldAccessType() throws Exception {
+ CtClass cc = sloader.get("test3.FieldAccessType");
+ CtMethod m1 = cc.getDeclaredMethod("access");
+ final boolean[] result = new boolean[1];
+ result[0] = true;
+ ExprEditor e = new ExprEditor() {
+ public void edit(FieldAccess fa) throws CannotCompileException {
+ if (!fa.getSignature().equals("[I"))
+ result[0] = false;
+ }
+ };
+ m1.instrument(e);
+ assertTrue(result[0]);
+ }
+
+ public void testGetNestedClasses() throws Exception {
+ CtClass cc = sloader.get("test3.NestedClass");
+ CtClass[] nested = cc.getNestedClasses();
+ assertEquals(4, nested.length);
+ testGetNestedClasses("test3.NestedClass$Inner", nested);
+ testGetNestedClasses("test3.NestedClass$StaticNested", nested);
+ testGetNestedClasses("test3.NestedClass$1Local", nested);
+ testGetNestedClasses("test3.NestedClass$1", nested);
+ }
+
+ private void testGetNestedClasses(String name, CtClass[] classes) {
+ for (int i = 0; i < classes.length; i++)
+ if (classes[i].getName().equals(name))
+ return;
+
+ fail("no class: " + name);
+ }
+
+ public void testGetParmeterAnnotations() throws Exception {
+ CtClass cc = sloader.get("test3.ParamAnno");
+ Object[][] anno = cc.getDeclaredMethod("foo").getParameterAnnotations();
+ assertEquals(4, anno.length);
+ assertEquals(0, anno[0].length);
+ assertEquals(0, anno[1].length);
+ assertEquals(0, anno[2].length);
+ assertEquals(0, anno[3].length);
+
+ Object[][] anno2 = cc.getDeclaredMethod("bar").getParameterAnnotations();
+ assertEquals(0, anno2.length);
+
+ Class rc = Class.forName("test3.ParamAnno");
+ java.lang.reflect.Method[] ms = rc.getDeclaredMethods();
+ java.lang.reflect.Method m1, m2;
+ if (ms[0].getName().equals("foo")) {
+ m1 = ms[0];
+ m2 = ms[1];
+ }
+ else {
+ m1 = ms[1];
+ m2 = ms[0];
+ }
+
+ java.lang.annotation.Annotation[][] ja;
+ ja = m1.getParameterAnnotations();
+ assertEquals(4, ja.length);
+ assertEquals(0, ja[0].length);
+ assertEquals(0, ja[1].length);
+ assertEquals(0, ja[2].length);
+ assertEquals(0, ja[3].length);
+
+ assertEquals(0, m2.getParameterAnnotations().length);
+ }
+
+ public void testSetModifiers() throws Exception {
+ CtClass cc = sloader.get("test3.SetModifiers");
+ try {
+ cc.setModifiers(Modifier.STATIC | Modifier.PUBLIC);
+ fail("static public class SetModifiers");
+ }
+ catch (RuntimeException e) {
+ assertEquals("cannot change test3.SetModifiers into a static class", e.getMessage());
+ }
+
+ cc = sloader.get("test3.SetModifiers$A");
+ cc.setModifiers(Modifier.STATIC | Modifier.PUBLIC);
+ assertTrue(Modifier.isStatic(cc.getModifiers()));
+ assertTrue((cc.getClassFile2().getAccessFlags() & AccessFlag.STATIC) == 0);
+ }
+
+ public void testFieldCopy() throws Exception {
+ CtClass cc = sloader.get("test3.FieldCopy");
+ CtClass cc2 = sloader.get("test3.FieldCopy2");
+ CtField f = cc.getDeclaredField("foo");
+ cc2.addField(new CtField(f, cc2));
+ CtField f2 = cc2.getDeclaredField("foo");
+ Object[] anno = f2.getAnnotations();
+ assertTrue(anno[0] instanceof test3.FieldCopy.Test);
+ assertEquals(Modifier.PRIVATE | Modifier.STATIC,
+ f2.getModifiers());
+ }
+
+ public void testMethodRedirect() throws Exception {
+ CtClass cc = sloader.get("test3.MethodRedirect");
+ CtClass cc2 = sloader.get("test3.MethodRedirectIntf");
+ CtMethod foo = cc.getDeclaredMethod("foo");
+ CtMethod poi = cc.getDeclaredMethod("poi");
+ CtMethod bar = cc.getDeclaredMethod("bar");
+ CtMethod afo = cc2.getDeclaredMethod("afo");
+ CodeConverter conv = new CodeConverter();
+
+ try {
+ conv.redirectMethodCall(foo, bar);
+ fail("foo");
+ }
+ catch (CannotCompileException e) {}
+
+ try {
+ conv.redirectMethodCall(poi, bar);
+ fail("bar");
+ }
+ catch (CannotCompileException e) {}
+
+ try {
+ conv.redirectMethodCall(bar, afo);
+ fail("afo");
+ }
+ catch (CannotCompileException e) {}
+ bar.setName("bar2");
+ conv.redirectMethodCall("bar", bar);
+ cc.instrument(conv);
+ // cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(2, invoke(obj, "test"));
+ }
+
+ public void testMethodRedirect2() throws Exception {
+ CtClass cc = sloader.get("test3.MethodRedirect2");
+ CtClass sup = sloader.get("test3.MethodRedirect2Sup");
+ CtClass supsup = sloader.get("test3.MethodRedirect2SupSup");
+ CtClass intf = sloader.get("test3.MethodRedirect2SupIntf");
+ CtMethod bfo2 = supsup.getDeclaredMethod("bfo2");
+ CtMethod afo2 = sup.getDeclaredMethod("afo2");
+ CtMethod foo = intf.getDeclaredMethod("foo");
+ CodeConverter conv = new CodeConverter();
+
+ conv.redirectMethodCall("bfo", bfo2);
+ conv.redirectMethodCall("afo", afo2);
+ conv.redirectMethodCall("bar", foo);
+ conv.redirectMethodCall("bar2", foo);
+ cc.instrument(conv);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(524, invoke(obj, "test"));
+ }
+
+ public void testClassMap() throws Exception {
+ ClassMap map = new ClassMap();
+ map.put("aa", "AA");
+ map.put("xx", "XX");
+ assertEquals("AA", map.get("aa"));
+ assertEquals(null, map.get("bb"));
+ ClassMap map2 = new ClassMap(map);
+ map2.put("aa", "A1");
+ map2.put("cc", "CC");
+ assertEquals("A1", map2.get("aa"));
+ assertEquals("CC", map2.get("cc"));
+ assertEquals("XX", map2.get("xx"));
+ assertEquals(null, map2.get("bb"));
+ }
+
+ public void testEmptyConstructor() throws Exception {
+ CtClass cc = sloader.get("test3.EmptyConstructor");
+ CtConstructor[] cons = cc.getDeclaredConstructors();
+ for (int i = 0; i < cons.length; i++)
+ assertTrue("index: " + i, cons[i].isEmpty());
+
+ cc = sloader.get("test3.EmptyConstructor2");
+ cons = cc.getDeclaredConstructors();
+ for (int i = 0; i < cons.length; i++)
+ assertTrue("index: " + i, cons[i].isEmpty());
+
+ cc = sloader.get("test3.EmptyConstructor3");
+ cons = cc.getDeclaredConstructors();
+ for (int i = 0; i < cons.length; i++)
+ assertTrue("index: " + i, cons[i].isEmpty());
+
+ cc = sloader.get("test3.EmptyConstructor4");
+ cons = cc.getDeclaredConstructors();
+ for (int i = 0; i < cons.length; i++)
+ assertFalse("index: " + i, cons[i].isEmpty());
+ }
+
+ public void testTransformRead() throws Exception {
+ CtClass cc = sloader.get("test3.TransformRead");
+ CtClass parent = cc.getSuperclass();
+ CtMethod m = cc.getDeclaredMethod("foo");
+ CodeConverter conv = new CodeConverter();
+ conv.replaceFieldRead(parent.getField("value"), cc, "getValue");
+ conv.replaceFieldRead(parent.getField("value2"), cc, "getValue2");
+ m.instrument(conv);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(11100, invoke(obj, "foo"));
+ }
+
+ public void testDescriptor() throws Exception {
+ assertEquals("int", Descriptor.toString("I"));
+ assertEquals("long[][]", Descriptor.toString("[[J"));
+ assertEquals("java.lang.Object", Descriptor.toString("Ljava/lang/Object;"));
+ assertEquals("()", Descriptor.toString("()V"));
+ assertEquals("(int)", Descriptor.toString("(I)V"));
+ assertEquals("(int,int[])", Descriptor.toString("(I[I)V"));
+ assertEquals("(java.lang.String,Foo[][])", Descriptor.toString("(Ljava/lang/String;[[LFoo;)V"));
+ }
+
+ public void testLongName() throws Exception {
+ CtClass cc = sloader.get("test3.Name");
+ assertEquals("test3.Name.<clinit>()", cc.getClassInitializer().getLongName());
+ assertEquals("test3.Name()", cc.getConstructor("()V").getLongName());
+ assertEquals("test3.Name(int)", cc.getConstructor("(I)V").getLongName());
+ assertEquals("test3.Name(test3.Name)", cc.getConstructor("(Ltest3/Name;)V").getLongName());
+ assertEquals("test3.Name(test3.Name,java.lang.String)",
+ cc.getConstructor("(Ltest3/Name;Ljava/lang/String;)V").getLongName());
+
+ assertEquals("test3.Name.foo()", cc.getDeclaredMethod("foo").getLongName());
+ assertEquals("test3.Name.foo2(int)", cc.getDeclaredMethod("foo2").getLongName());
+ assertEquals("test3.Name.foo3(java.lang.String)", cc.getDeclaredMethod("foo3").getLongName());
+ assertEquals("test3.Name.foo4(java.lang.String[])", cc.getDeclaredMethod("foo4").getLongName());
+ assertEquals("test3.Name.foo5(int,java.lang.String)", cc.getDeclaredMethod("foo5").getLongName());
+ }
+
+ public void testPackageName() throws Exception {
+ CtClass cc = sloader.get("test3.PackName");
+ CtMethod m1 = CtNewMethod.make(
+ "public int run() {" +
+ " return test3.PackName.get() + test3.sub.SubPackName.get(); }",
+ cc);
+ cc.addMethod(m1);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(8, invoke(obj, "run"));
+ }
+
+ public void testErasure() throws Exception {
+ CtClass cc = sloader.get("test3.Erasure");
+ cc.addInterface(sloader.get("test3.ErasureGet"));
+ CtMethod m1 = CtNewMethod.make(
+ "public Object get() { return value; }",
+ cc);
+ cc.addMethod(m1);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(4, invoke(obj, "run"));
+ }
+
+ /* Check the result of JvstTest#testFieldInit()
+ * This tests CtClassType#saveClassFile().
+ */
+ public void testFieldInitAgain() throws Exception {
+ System.gc();
+ CtClass cc = sloader.get("test1.FieldInit");
+ CtField f = cc.getDeclaredField("f1");
+ assertEquals(CtClass.intType, f.getType());
+ assertTrue("f.hashCode() doesn't change!", f.hashCode() != JvstTest.testFieldInitHash);
+ }
+
+ /* This tests CtClassType#saveClassFile().
+ * A CtMethod is not garbage collected, its CtClass is never
+ * compressed.
+ */
+ public void testCalleeBeforeAgain() throws Exception {
+ CtClass cc = sloader.get("test1.CalleeBefore");
+ assertEquals(JvstTest.testCalleeBeforeMethod,
+ cc.getDeclaredMethod("m1"));
+ assertEquals(JvstTest.testCalleeBeforeMethod2,
+ cc.getDeclaredMethod("m2").getMethodInfo2().hashCode());
+ }
+
+ public void testSetSuper() throws Exception {
+ CtClass cc = sloader.get("test3.Superclass");
+ CtClass cc3 = sloader.get("test3.Superclass3");
+ cc3.setModifiers(Modifier.setPublic(cc3.getModifiers()));
+ cc.setSuperclass(sloader.get("test3.Superclass3"));
+ cc.writeFile();
+ cc3.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(21, invoke(obj, "test"));
+ }
+
+ public void testFrozen2() throws Exception {
+ CtClass cc = sloader.get("test3.Frozen");
+ cc.addField(new CtField(CtClass.intType, "k", cc));
+ cc.toBytecode();
+ cc.toBytecode();
+ cc = sloader.makeClass("test3.Frozen2");
+ cc.toBytecode();
+ cc.toBytecode();
+ }
+
+ public void testCopyAnnotation() throws Exception {
+ CtClass cc1 = sloader.get("test3.CopyAnnoBase");
+ CtMethod m1 = cc1.getDeclaredMethod("getX");
+ CtClass cc2 = sloader.get("test3.CopyAnno");
+ CtMethod m2 = cc2.getDeclaredMethod("getX");
+ copyAnnotations(m1, m2);
+ cc2.getClassFile();
+ Class clazz = cc2.toClass(DefineClassCapability.class);
+ java.lang.reflect.Method m = clazz.getDeclaredMethod("getX", new Class[0]);
+ assertEquals(1, m.getAnnotations().length);
+ test3.VisibleAnno a = m.getAnnotation(test3.VisibleAnno.class);
+ assertNotNull(a);
+ }
+
+ private void copyAnnotations(CtMethod src, CtMethod dest)
+ throws NotFoundException
+ {
+ MethodInfo srcInfo = src.getMethodInfo2();
+ MethodInfo destInfo = dest.getMethodInfo2();
+ copyAnnotations(srcInfo, destInfo, AnnotationsAttribute.invisibleTag);
+ copyAnnotations(srcInfo, destInfo, AnnotationsAttribute.visibleTag);
+ }
+
+ private void copyAnnotations(MethodInfo src, MethodInfo dest, String annotationTag) {
+ AnnotationsAttribute attribute = (AnnotationsAttribute)src.getAttribute(annotationTag);
+ if (attribute != null)
+ dest.addAttribute(attribute.copy(dest.getConstPool(), new java.util.HashMap()));
+ }
+
+ public void testStaticFinalField() throws Exception {
+ CtClass cc = sloader.makeClass("test3.StaticFinalField");
+ CtField fj = new CtField(CtClass.longType, "j", cc);
+ fj.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
+ cc.addField(fj, CtField.Initializer.constant(2L));
+ CtField fi = new CtField(CtClass.intType, "i", cc);
+ fi.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
+ cc.addField(fi, CtField.Initializer.constant(3));
+ CtField fs = new CtField(CtClass.shortType, "s", cc);
+ fs.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
+ cc.addField(fs, CtField.Initializer.constant(4));
+ CtField fc = new CtField(CtClass.charType, "c", cc);
+ fc.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
+ cc.addField(fc, CtField.Initializer.constant('5'));
+ CtField fby = new CtField(CtClass.byteType, "by", cc);
+ fby.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
+ cc.addField(fby, CtField.Initializer.constant(6));
+ CtField fb = new CtField(CtClass.booleanType, "b", cc);
+ fb.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
+ cc.addField(fb, CtField.Initializer.constant(true));
+ CtField ff = new CtField(CtClass.floatType, "f", cc);
+ ff.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
+ cc.addField(ff, CtField.Initializer.constant(7.0F));
+ CtField fstr = new CtField(sloader.get("java.lang.String"), "str", cc);
+ fstr.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
+ cc.addField(fstr, CtField.Initializer.constant("foo"));
+ CtField fobj = new CtField(sloader.get("java.lang.Object"), "obj", cc);
+ fobj.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
+ cc.addField(fobj, CtField.Initializer.constant("bar"));
+
+ cc.writeFile();
+ Class clazz = cc.toClass(DefineClassCapability.class);
+ assertEquals(2L, clazz.getField("j").getLong(null));
+ assertEquals(3, clazz.getField("i").getInt(null));
+ assertEquals(4, clazz.getField("s").getShort(null));
+ assertEquals('5', clazz.getField("c").getChar(null));
+ assertEquals(6, clazz.getField("by").getByte(null));
+ assertEquals(true, clazz.getField("b").getBoolean(null));
+ assertEquals(7.0F, clazz.getField("f").getFloat(null));
+ assertEquals("foo", clazz.getField("str").get(null));
+ assertEquals("bar", clazz.getField("obj").get(null));
+ }
+
+ /*
+ public void testClassPath() throws Exception {
+ ClassPool cp = new ClassPool(null);
+ cp.appendClassPath("./test-classpath.JaR");
+ assertNotNull(cp.get("test.Point"));
+ cp = new ClassPool(null);
+ cp.appendClassPath("./*");
+ assertNotNull(cp.get("javassist.bytecode.Gap0Example"));
+ }*/
+
+ public void testVoidReturn() throws Exception {
+ CtClass cc = sloader.get("test3.VoidReturn");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ m1.insertAfter("System.out.println(\"return value: \" + $_);", true);
+ cc.writeFile();
+ make(cc.getName());
+ }
+
+ public void testInsertParam0() throws Exception {
+ assertEquals("(I)V", Descriptor.insertParameter(CtClass.intType, "()V"));
+ assertEquals("(ILjava/lang/Object;)V", Descriptor.insertParameter(CtClass.intType, "(Ljava/lang/Object;)V"));
+ assertEquals("(IJ)V", Descriptor.appendParameter(CtClass.longType, "(I)V"));
+ assertEquals("(Ljava/lang/String;I)V", Descriptor.insertParameter(sloader.get("java.lang.String"), "(I)V"));
+ }
+
+ public void testInsertParam() throws Exception {
+ CtClass cc = sloader.get("test3.InsParam");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ m1.insertParameter(CtClass.longType);
+ m1.insertBefore("$2 += (int)$1;");
+
+ CtMethod m2 = cc.getDeclaredMethod("bar");
+ m2.addParameter(CtClass.doubleType);
+ m2.insertBefore("$1 += (int)$2;");
+
+ CtMethod m3 = cc.getDeclaredMethod("poi");
+ m3.addParameter(sloader.get("java.lang.Object"));
+ m3.insertBefore("$2 = (String)$3;");
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(11, invoke(obj, "foo", 10L, 1));
+ assertEquals(11, invoke(obj, "bar", 1, 10.0));
+ assertEquals(3, invoke(obj, "poi", 1, "x", "xx"));
+ }
+
+ private int invoke(Object target, String method, long arg1, int arg2)
+ throws Exception
+ {
+ java.lang.reflect.Method m =
+ target.getClass().getMethod(method, new Class[] { long.class, int.class });
+ Object res = m.invoke(target, new Object[] { Long.valueOf(arg1), Integer.valueOf(arg2)});
+ return ((Integer)res).intValue();
+ }
+
+ private int invoke(Object target, String method, int arg1, double arg2)
+ throws Exception
+ {
+ java.lang.reflect.Method m =
+ target.getClass().getMethod(method, new Class[] { int.class, double.class });
+ Object res = m.invoke(target, new Object[] { Integer.valueOf(arg1), Double.valueOf(arg2)});
+ return ((Integer)res).intValue();
+ }
+
+ private int invoke(Object target, String method, int arg1, String arg2, Object arg3)
+ throws Exception
+ {
+ java.lang.reflect.Method m =
+ target.getClass().getMethod(method, new Class[] { int.class, String.class, Object.class });
+ Object res = m.invoke(target, new Object[] { Integer.valueOf(arg1), arg2, arg3});
+ return ((Integer)res).intValue();
+ }
+
+ public void testInvokeinterface() throws Exception {
+ // JIRA JASSIST-60
+ CtClass cc = sloader.get("test3.InvokeIntf");
+ CtMethod mth = cc.getDeclaredMethod("doit");
+ ExprEditor e = new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ String block = "{$_=$proceed($$);}";
+ m.replace(block);
+ }
+ };
+ mth.instrument(e);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(7, invoke(obj, "test"));
+ }
+
+ public void testInvokeArrayObj() throws Exception {
+ // JIRA JASSIST-61
+ CtClass cc = sloader.get("test3.InvokeArray");
+ CtMethod mth = cc.getDeclaredMethod("doit");
+ ExprEditor e = new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ String block = "{$_=$proceed($$);}";
+ m.replace(block);
+ }
+ };
+ mth.instrument(e);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "test"));
+ }
+
+ public void testNewExprTry() throws Exception {
+ CtClass cc = sloader.get("test3.NewExprTryCatch");
+ CtMethod mth = cc.getDeclaredMethod("instrumentMe");
+ ExprEditor e = new ExprEditor() {
+ public void edit(NewExpr m) throws CannotCompileException {
+ String block = "{$_=$proceed($$);}";
+ m.replace(block);
+ }
+ };
+ mth.instrument(e);
+
+ // JIRA JASSIST-52
+ CtMethod mth2 = cc.getDeclaredMethod("me2");
+ ExprEditor e2 = new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ String block = "{try{$_=$proceed($$);} catch(Throwable t) {}}";
+ m.replace(block);
+ }
+ };
+ //mth2.instrument(e2);
+
+ /*
+ // JIRA JASSIST-53
+ CtClass cc2 = sloader.get("test3.NewExprTryCatch2");
+ CtBehavior mth3 = cc2.getDeclaredConstructors()[0];
+ ExprEditor e3 = new ExprEditor() {
+ public void edit(ConstructorCall m) throws CannotCompileException {
+ String block = "{try {$_=$proceed($$);} catch (Throwable t) {}}";
+ m.replace(block);
+ }
+ };
+ mth3.instrument(e3);
+ */
+
+ cc.writeFile();
+ // cc2.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(0, invoke(obj, "test"));
+ // Object obj2 = make(cc2.getName());
+ }
+
+ public void testJIRA63() throws Exception {
+ frameTypeTest(2);
+ frameTypeTest(7);
+ }
+
+ private void frameTypeTest(final int initializerRepeatCount) throws Exception {
+ // Get a class
+ final CtClass cTst = sloader.get("test3.JIRA63");
+ cTst.getClassFile().setMajorVersion(ClassFile.JAVA_6);
+ try {
+ // Create an initializer for the fields
+ String initializer = "test3.JIRA63Helper";
+ for(int i=0; i < initializerRepeatCount; i++)
+ initializer += ".getAnObject(new Integer(1))";
+
+ initializer += ";";
+
+ // Add some fields
+ final CtClass cObj = sloader.get("java.lang.Object");
+ for(int i = 0; i < 7; i++)
+ cTst.addField(new CtField(cObj, "a" + i, cTst), initializer);
+
+ // Modify the constructors
+ for(final CtConstructor m : cTst.getConstructors()) {
+ m.insertAfter(initializer, true);
+ m.insertBefore(initializer);
+ }
+
+ // Get the byte code.
+ // cTst.toBytecode();
+ cTst.writeFile();
+ //make(cTst.getName());
+ }
+ finally {
+ cTst.detach();
+ }
+ }
+
+ public void testInsertBeforeType() throws Exception {
+ CtClass cc = sloader.get("test3.InsertBeforeType");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ m1.insertBefore("value = $type.getName();");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(5, invoke(obj, "test"));
+ }
+
+ public void testTransformNewClass() throws Exception {
+ CodeConverter conv = new CodeConverter();
+ conv.replaceNew(sloader.get("test3.TransNewClassOld"),
+ sloader.get("test3.TransNewClassNew"));
+ CtClass cc = sloader.get("test3.TransNewClass");
+ cc.instrument(conv);
+ cc.writeFile();
+
+ CtClass cc2 = sloader.get("test3.TransNewClass$TransNewClass2");
+ cc2.instrument(conv);
+ cc2.writeFile();
+
+ Object obj = make(cc.getName());
+ assertEquals(170, invoke(obj, "test"));
+ Object obj2 = make(cc2.getName());
+ assertEquals(50, invoke(obj2, "test"));
+ }
+
+ public void testInsertAfter() throws Exception {
+ CtClass cc = sloader.get("test3.InsertAfter");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ m1.insertAfter("k++;", true);
+ CtConstructor cons = cc.getConstructor("()V");
+ cons.insertAfter("k++;", true);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(6, invoke(obj, "test"));
+ }
+
+ public void testInsertSwitch() throws Exception {
+ CtClass cc = sloader.get("test3.Switch");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ String sourceCode = "{"
+ + "System.out.println(\"Bla!\");"
+ + "}";
+ String toInsert =
+ " try " +
+ " { " +
+ sourceCode +
+ " } " +
+ " catch(Throwable e) " +
+ " { " +
+ " e.printStackTrace(); " +
+ " } ";
+
+ m1.insertBefore(toInsert);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(0, invoke(obj, "test"));
+ }
+
+ public void testStringBuilder() throws Exception {
+ CtClass cc = sloader.get("test3.StrBuild");
+ CtMethod mth = cc.getDeclaredMethod("test");
+ ExprEditor ed = new ExprEditor() {
+ public void edit(MethodCall m) throws CannotCompileException {
+ String block = "$_=$proceed($$);";
+ m.replace(block);
+ }
+ };
+ mth.instrument(ed);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals('t', invoke(obj, "test"));
+ }
+
+ public void testInheritCons() throws Exception {
+ CtClass s = sloader.get("test3.InheritCons");
+ CtClass cc = sloader.makeClass("test3.InheritCons2", s);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals("test3.InheritCons2", obj.getClass().getName());
+ assertEquals(3, obj.getClass().getDeclaredConstructors().length);
+
+ cc = sloader.makeClass("InheritCons3", s);
+ cc.writeFile();
+ obj = make(cc.getName());
+ assertEquals("InheritCons3", obj.getClass().getName());
+ assertEquals(2, obj.getClass().getDeclaredConstructors().length);
+ }
+
+ public void testAddInterfaceMethod() throws Exception {
+ CtClass cc = sloader.makeInterface("test3.AddIntfMth");
+ CtMethod m = CtMethod.make("void foo();", cc);
+ cc.addMethod(m);
+ assertTrue(Modifier.isPublic(m.getModifiers()));
+ CtMethod m2 = CtMethod.make("public void foo2();", cc);
+ cc.addMethod(m2);
+ assertTrue(Modifier.isPublic(m2.getModifiers()));
+ CtMethod m3 = CtMethod.make("public void foo3() {}", cc);
+ try {
+ cc.addMethod(m3);
+ if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_8)
+ fail();
+ }
+ catch (CannotCompileException e) {
+ // System.out.println(e);
+ }
+ }
+
+ // JIRA-67
+ public void test67() throws Exception {
+ ClassPool pool = new ClassPool(true);
+ CtClass ctc = pool.makeClass("test3.Clazz67");
+ StringBuilder sb = new StringBuilder("public void test() {");
+ for (int i = 0; i < 1000; i++) {
+ sb.append("for(int i=0; i<10; i++) {}"); // line 1
+ // sb.append("for(int i=0; i<10; i++) {int j=i;}"); // line 2
+ }
+
+ sb.append("}");
+ ctc.addMethod(CtNewMethod.make(sb.toString(), ctc));
+ ctc.debugWriteFile();
+ ctc.toClass(DefineClassCapability.class).getConstructor().newInstance();
+ }
+
+ // JIRA-83
+ public void testEmptyCatch() throws Exception {
+ CtClass cc = sloader.get("test3.EmptyCatch");
+ CtMethod mth = cc.getDeclaredMethod("test");
+ mth.instrument(new ExprEditor() {
+ public void edit(Handler h) throws CannotCompileException {
+ try {
+ assertEquals(null, h.getType());
+ assertTrue(h.isFinally());
+ } catch (NotFoundException e) {}
+ }
+ });
+ }
+}
diff --git a/src/test/javassist/JvstTest4.java b/src/test/javassist/JvstTest4.java
new file mode 100644
index 0000000..7c8c745
--- /dev/null
+++ b/src/test/javassist/JvstTest4.java
@@ -0,0 +1,1119 @@
+package javassist;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.HashSet;
+
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+
+import javassist.bytecode.*;
+import javassist.bytecode.annotation.Annotation;
+import javassist.expr.*;
+
+@SuppressWarnings({"rawtypes","unchecked","unused"})
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class JvstTest4 extends JvstTestRoot {
+ public JvstTest4(String name) {
+ super(name);
+ }
+
+ public void testInsertLocalVars() throws Exception {
+ CtClass cc = sloader.get("test4.LocalVars");
+
+ CtMethod m1 = cc.getDeclaredMethod("run");
+ m1.getMethodInfo().getCodeAttribute().insertLocalVar(2, 20);
+ m1.getMethodInfo().rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile());
+ CtMethod m2 = cc.getDeclaredMethod("run2");
+ m2.getMethodInfo().getCodeAttribute().insertLocalVar(2, 0x101);
+ m2.getMethodInfo().rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile());
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(10, invoke(obj, "run"));
+ assertEquals(10, invoke(obj, "run2"));
+ }
+
+ public void testCodeConv() throws Exception {
+ CtClass cc = sloader.get("test4.CodeConv");
+ CtMethod m1 = cc.getDeclaredMethod("m1");
+ CtMethod m2 = cc.getDeclaredMethod("m2");
+ CtMethod m3 = cc.getDeclaredMethod("m3");
+ CodeConverter conv = new CodeConverter();
+ conv.insertAfterMethod(m1, m3);
+ conv.insertBeforeMethod(m2, m3);
+ cc.instrument(conv);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(111033, invoke(obj, "run"));
+ }
+
+ public void testCodeConv2() throws Exception {
+ CtClass cc = sloader.get("test4.CodeConv2");
+ CtField f = cc.getDeclaredField("field");
+ CtField f2 = cc.getDeclaredField("sf");
+ CtMethod run = cc.getDeclaredMethod("run");
+ CodeConverter conv = new CodeConverter();
+ conv.replaceFieldRead(f, cc, "read");
+ conv.replaceFieldWrite(f, cc, "write");
+ conv.replaceFieldRead(f2, cc, "read");
+ conv.replaceFieldWrite(f2, cc, "write");
+ run.instrument(conv);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(14001600, invoke(obj, "run"));
+ }
+
+ public void testInsGap() throws Exception {
+ CtClass cc = sloader.get("test4.GapSwitch");
+ ExprEditor ed = new ExprEditor() {
+ public void edit(MethodCall c) throws CannotCompileException {
+ c.replace("{ value++; $_ = $proceed($$); }");
+ }
+ };
+
+ CtMethod m1 = cc.getDeclaredMethod("run");
+ m1.instrument(ed);
+ CtMethod m2 = cc.getDeclaredMethod("run2");
+ m2.instrument(ed);
+
+ final CtMethod m3 = cc.getDeclaredMethod("run3");
+ m3.instrument(new ExprEditor() {
+ public void edit(MethodCall c) throws CannotCompileException {
+ CodeIterator it = m3.getMethodInfo().getCodeAttribute().iterator();
+ try {
+ it.insertGap(c.indexOfBytecode(), 5000);
+ } catch (BadBytecode e) {
+ throw new CannotCompileException(e);
+ }
+ }
+ });
+ m3.getMethodInfo().rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile());
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1010, invoke(obj, "run"));
+ assertEquals(1100, invoke(obj, "run2"));
+ assertEquals(12222, invoke(obj, "run3"));
+ }
+
+ public void testAnnotationCheck() throws Exception {
+ CtClass cc = sloader.get("test4.Anno");
+ CtMethod m1 = cc.getDeclaredMethod("foo");
+ CtField f = cc.getDeclaredField("value");
+
+ assertTrue(cc.hasAnnotation(test4.Anno1.class));
+ assertFalse(cc.hasAnnotation(java.lang.annotation.Documented.class));
+
+ assertTrue(cc.hasAnnotation(test4.Anno1.class.getName()));
+ assertFalse(cc.hasAnnotation(java.lang.annotation.Documented.class.getName()));
+
+ assertEquals("empty", ((test4.Anno1)cc.getAnnotation(test4.Anno1.class)).value());
+ assertNull(cc.getAnnotation(Deprecated.class));
+
+ assertTrue(m1.hasAnnotation(test4.Anno1.class));
+ assertFalse(m1.hasAnnotation(java.lang.annotation.Documented.class));
+ assertTrue(m1.getAnnotation(test4.Anno1.class) != null);
+ assertNull(m1.getAnnotation(Deprecated.class));
+
+ assertTrue(f.hasAnnotation(test4.Anno1.class));
+ assertFalse(f.hasAnnotation(java.lang.annotation.Documented.class));
+ assertTrue(f.getAnnotation(test4.Anno1.class) != null);
+ assertNull(f.getAnnotation(Deprecated.class));
+ }
+
+ public void testRename() throws Exception {
+ CtClass cc = sloader.get("test4.Rename");
+ cc.setName("test4.Rename2");
+ cc.rebuildClassFile();
+ cc.writeFile();
+ CtClass cc2 = sloader.get("test4.IRename");
+ cc2.replaceClassName("test4.Rename", "test4.Rename2");
+ cc2.rebuildClassFile();
+ cc2.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals("test4.Rename2", obj.getClass().getName());
+ assertEquals(14, invoke(obj, "run"));
+ }
+
+ public void testRename2() throws Exception {
+ CtClass cc = sloader.get("test4.Signature");
+ cc.setName("test4.Sig");
+ cc.rebuildClassFile();
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "run"));
+ }
+
+ public void testJIRA93() throws Exception {
+ ClassPool cp = ClassPool.getDefault();
+ CtClass cc = sloader.getCtClass("test4.JIRA93");
+ CtMethod m = cc.getDeclaredMethod("foo");
+
+ m.addLocalVariable("bar", CtClass.longType);
+ // The original bug report includes the next line.
+ // But this is not a bug.
+ //m.insertAfter("bar;", true);
+ // Instead, the following code is OK.
+ m.insertBefore("bar = 0;");
+ m.insertAfter("bar;", false);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ }
+
+ public void testNewRemover() throws Exception {
+ CtClass cc = sloader.get("test4.NewRemover");
+ CtMethod mth = cc.getDeclaredMethod("make");
+ mth.getMethodInfo().rebuildStackMap(cc.getClassPool());
+ mth.getMethodInfo().rebuildStackMapForME(cc.getClassPool());
+ //cc.debugWriteFile("debug");
+ CodeConverter conv = new CodeConverter();
+ conv.replaceNew(cc, cc, "make2");
+ mth.instrument(conv);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(10, invoke(obj, "run"));
+ }
+
+ public void testClassFileWriter() throws Exception {
+ ClassFileWriter cfw = new ClassFileWriter(ClassFile.JAVA_4, 0);
+ ClassFileWriter.ConstPoolWriter cpw = cfw.getConstPool();
+
+ ClassFileWriter.FieldWriter fw = cfw.getFieldWriter();
+ fw.add(AccessFlag.PUBLIC, "value", "J", null);
+ fw.add(AccessFlag.PROTECTED | AccessFlag.STATIC, "value2", "Ljava/lang/String;", null);
+
+ ClassFileWriter.MethodWriter mw = cfw.getMethodWriter();
+
+ mw.begin(AccessFlag.PUBLIC, MethodInfo.nameInit, "()V", null, null);
+ assertEquals(0, mw.size());
+ mw.add(Opcode.ALOAD_0);
+ assertEquals(1, mw.size());
+ mw.addInvoke(Opcode.INVOKESPECIAL, "java/lang/Object", MethodInfo.nameInit, "()V");
+ mw.add(Opcode.RETURN);
+ mw.codeEnd(1, 1);
+ mw.end(null, null);
+
+ mw.begin(AccessFlag.PUBLIC, "move", "(II)V", null, null);
+ assertEquals(0, mw.size());
+ mw.add(Opcode.ALOAD_0);
+ mw.addInvoke(Opcode.INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;");
+ assertEquals(4, mw.size());
+ mw.add(Opcode.POP);
+ mw.add(Opcode.RETURN);
+ mw.add(Opcode.POP);
+ mw.add(Opcode.RETURN);
+ mw.codeEnd(1, 3);
+ mw.addCatch(0, 4, 6, cpw.addClassInfo("java/lang/Exception"));
+ mw.addCatch(0, 4, 6, cpw.addClassInfo("java/lang/Throwable"));
+ mw.end(null, null);
+
+ String[] exceptions = { "java/lang/Exception", "java/lang/NullPointerException" };
+ mw.begin(AccessFlag.PUBLIC, "move2", "()V", exceptions, null);
+ mw.add(Opcode.RETURN);
+ mw.codeEnd(0, 1);
+ StackMapTable.Writer stack = new StackMapTable.Writer(32);
+ stack.sameFrame(1);
+ mw.end(stack, null);
+
+ mw.begin(AccessFlag.PUBLIC, "foo", "()I", null, null);
+ mw.add(Opcode.ICONST_2);
+ mw.add(Opcode.IRETURN);
+ mw.codeEnd(1, 1);
+ mw.end(null, null);
+
+ byte[] out = cfw.end(AccessFlag.PUBLIC, cpw.addClassInfo("test4/WrittenFile"),
+ cpw.addClassInfo("java/lang/Object"),
+ null, null);
+ FileOutputStream fos = new FileOutputStream("test4/WrittenFile.class");
+ fos.write(out);
+ fos.close();
+ Object obj = make("test4.WrittenFile");
+ assertNotNull(obj);
+ assertEquals(2, invoke(obj, "foo"));
+ }
+
+ public void testClassFileWriter2() throws Exception {
+ ClassFileWriter cfw = new ClassFileWriter(ClassFile.JAVA_4, 0);
+ ClassFileWriter.ConstPoolWriter cpw = cfw.getConstPool();
+
+ ClassFileWriter.FieldWriter fw = cfw.getFieldWriter();
+ fw.add(AccessFlag.PUBLIC | AccessFlag.STATIC, "value", "I", null);
+
+ ClassFileWriter.MethodWriter mw = cfw.getMethodWriter();
+
+ mw.begin(AccessFlag.PUBLIC, MethodInfo.nameInit, "()V", null, null);
+ mw.add(Opcode.ALOAD_0);
+ mw.addInvoke(Opcode.INVOKESPECIAL, "java/lang/Object", MethodInfo.nameInit, "()V");
+ mw.add(Opcode.RETURN);
+ mw.codeEnd(1, 1);
+ mw.end(null, null);
+
+ String[] exceptions = { "java/lang/Exception" };
+ mw.begin(AccessFlag.PUBLIC | AccessFlag.ABSTRACT, "move", "(II)V", exceptions, null);
+ mw.end(null, null);
+
+ int thisClass = cpw.addClassInfo("test4/WrittenFile2");
+ int superClass = cpw.addClassInfo("java/lang/Object");
+
+ cfw.end(new DataOutputStream(new FileOutputStream("test4/WrittenFile2.class")),
+ AccessFlag.PUBLIC | AccessFlag.ABSTRACT, thisClass, superClass,
+ null, null);
+
+ File f = new File("test4/WrittenFile2.class");
+ byte[] file = new byte[(int)f.length()];
+ FileInputStream fis = new FileInputStream(f);
+ fis.read(file);
+ fis.close();
+
+ byte[] out = cfw.end(AccessFlag.PUBLIC | AccessFlag.ABSTRACT, thisClass,
+ superClass, null, null);
+
+ assertEquals(out.length, file.length);
+ for (int i = 0; i < out.length; i++)
+ assertEquals(out[i], file[i]);
+
+ CtClass sub = dloader.makeClass("test4.WrittenFile2sub", dloader.get("test4.WrittenFile2"));
+ sub.addMethod(CtMethod.make("public void move(int i, int j) {}", sub));
+ sub.addMethod(CtMethod.make("public int foo() { move(0, 1); return 1; }", sub));
+ sub.writeFile();
+ Object obj = make("test4.WrittenFile2sub");
+ assertEquals(1, invoke(obj, "foo"));
+ }
+
+ public void testClassFileWriter3() throws Exception {
+ ClassFileWriter cfw = new ClassFileWriter(ClassFile.JAVA_4, 0);
+ ClassFileWriter.ConstPoolWriter cpw = cfw.getConstPool();
+ int superClass = cpw.addClassInfo("java/lang/Object");
+
+ final int syntheticTag = cpw.addUtf8Info("Synthetic");
+ ClassFileWriter.AttributeWriter attribute = new ClassFileWriter.AttributeWriter() {
+ public void write(DataOutputStream out) throws java.io.IOException {
+ out.writeShort(syntheticTag);
+ out.writeInt(0);
+ }
+
+ public int size() {
+ return 1;
+ }
+ };
+
+ ClassFileWriter.FieldWriter fw = cfw.getFieldWriter();
+ fw.add(AccessFlag.PUBLIC, "value", "J", null);
+ fw.add(AccessFlag.PROTECTED | AccessFlag.STATIC, "value2", "Ljava/lang/String;", attribute);
+
+ ClassFileWriter.MethodWriter mw = cfw.getMethodWriter();
+
+ mw.begin(AccessFlag.PUBLIC, MethodInfo.nameInit, "()V", null, attribute);
+ mw.add(Opcode.ALOAD_0);
+ mw.add(Opcode.INVOKESPECIAL);
+ mw.add16(cpw.addMethodrefInfo(superClass, cpw.addNameAndTypeInfo(MethodInfo.nameInit, "()V")));
+ // mw.addInvoke(Opcode.INVOKESPECIAL, "java/lang/Object", MethodInfo.nameInit, "()V");
+ mw.add(Opcode.RETURN);
+ mw.codeEnd(1, 1);
+ mw.end(null, null);
+
+ mw.begin(AccessFlag.PUBLIC, "foo", "()I", null, attribute);
+ mw.add(Opcode.ICONST_2);
+ mw.add(Opcode.IRETURN);
+ mw.codeEnd(1, 1);
+ mw.end(null, null);
+
+ int thisClass = cpw.addClassInfo("test4/WrittenFile3");
+ cfw.end(new DataOutputStream(new FileOutputStream("test4/WrittenFile3.class")),
+ AccessFlag.PUBLIC, thisClass, superClass,
+ null, attribute);
+
+ File f = new File("test4/WrittenFile3.class");
+ byte[] file = new byte[(int)f.length()];
+ FileInputStream fis = new FileInputStream(f);
+ fis.read(file);
+ fis.close();
+
+ byte[] out = cfw.end(AccessFlag.PUBLIC, thisClass, superClass,
+ null, attribute);
+
+ assertEquals(out.length, file.length);
+ for (int i = 0; i < out.length; i++)
+ assertEquals(out[i], file[i]);
+
+ Object obj = make("test4.WrittenFile3");
+ assertNotNull(obj);
+ assertEquals(2, invoke(obj, "foo"));
+ }
+
+ public void testCtArray() throws Exception {
+ CtClass cc = sloader.get("int");
+ assertEquals(Modifier.FINAL | Modifier.PUBLIC, cc.getModifiers());
+ cc = sloader.get("int[]");
+ assertEquals(Modifier.FINAL | Modifier.PUBLIC, cc.getModifiers());
+ cc = sloader.get("java.lang.String[]");
+ assertEquals(Modifier.FINAL | Modifier.PUBLIC, cc.getModifiers());
+ CtClass[] intfs = cc.getInterfaces();
+ assertEquals(Cloneable.class.getName(), intfs[0].getName());
+ assertEquals(java.io.Serializable.class.getName(), intfs[1].getName());
+ cc = sloader.get("test4.CtArrayTest[]");
+ assertEquals(Modifier.FINAL | Modifier.PUBLIC, cc.getModifiers());
+ }
+
+ public void testAnalysisType() throws Exception {
+ testAnalysisType2(sloader.get("int[]"), 1);
+ testAnalysisType2(sloader.get("java.lang.String[][]"), 2);
+ sloader.makeClass("A");
+ testAnalysisType2(sloader.getCtClass("A"), 0);
+ testAnalysisType2(sloader.getCtClass("A[]"), 1);
+ testAnalysisType2(sloader.getCtClass("A[][]"), 2);
+ }
+
+ private void testAnalysisType2(CtClass cc, int size) throws Exception {
+ javassist.bytecode.analysis.Type t = javassist.bytecode.analysis.Type.get(cc);
+ assertEquals(cc.getName(), size, t.getDimensions());
+ }
+
+ public void testArrayType() throws Exception {
+ CtClass at = sloader.get("java.lang.Object[]");
+ CtClass[] intfs = at.getInterfaces();
+ assertEquals(intfs.length, 2);
+ assertEquals(intfs[0].getName(), java.lang.Cloneable.class.getName());
+ assertEquals(intfs[1].getName(), java.io.Serializable.class.getName());
+
+ assertTrue(at.subtypeOf(sloader.get(java.lang.Object.class.getName())));
+ assertTrue(at.subtypeOf(intfs[0]));
+ assertTrue(at.subtypeOf(intfs[1]));
+ assertTrue(at.subtypeOf(intfs[1]));
+ CtClass subt = sloader.get(java.text.CharacterIterator.class.getName());
+ assertFalse(at.subtypeOf(subt));
+ }
+
+ public void testGetFieldDesc() throws Exception {
+ CtClass cc = sloader.get("test4.GetFieldDesc");
+ cc.getDeclaredField("f", "I");
+ cc.getField("s", "Ljava/lang/String;");
+ CtClass cc2 = sloader.get("test4.GetFieldDescSub");
+ assertEquals(cc2.getField("s", "Ljava/lang/String;").getDeclaringClass().getName(),
+ "test4.GetFieldDesc");
+ assertEquals(cc2.getField("s", "I").getDeclaringClass().getName(),
+ "test4.GetFieldDescSub");
+ }
+
+ public void testMakeMethod() throws CannotCompileException {
+ CtClass ctClass = sloader.makeClass("test4.MakeMethod2");
+ CtNewMethod.make("public String foox(){return test4.MakeMethod.foo();}", ctClass);
+ CtNewMethod.make("public String foo(){return test4.MakeMethod.foo();}", ctClass);
+ }
+
+ public void testVarArgs() throws Exception {
+ CtClass cc = sloader.get("test4.VarArgs");
+ CtMethod m = CtMethod.make("public int foo(int i, String[] args) { return args.length; }", cc);
+ m.setModifiers(m.getModifiers() | Modifier.VARARGS);
+ cc.addMethod(m);
+ m = CtMethod.make("public int run() { return goo(7, new int[] { 1, 2, 3 }); }", cc);
+ cc.addMethod(m);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "run"));
+ }
+
+ public void testGetAllRef() throws Exception {
+ CtClass cc = sloader.get("test4.GetAllRef");
+ ClassFile cf = cc.getClassFile();
+ AttributeInfo ainfo
+ = cf.getAttribute(AnnotationsAttribute.visibleTag);
+ ClassMap map = new ClassMap();
+ map.put("test4.GetAllRefAnno", "test4.GetAllRefAnno2");
+ map.put("test4.GetAllRefEnum", "test4.GetAllRefEnum2");
+ map.put("java.lang.String", "java.lang.StringBuilder");
+ cf.addAttribute(ainfo.copy(cf.getConstPool(), map));
+ cc.writeFile();
+ cc.detach();
+
+ cc = dloader.get(cc.getName());
+ test4.GetAllRefAnno2 anno
+ = (test4.GetAllRefAnno2)cc.getAnnotation(test4.GetAllRefAnno2.class);
+ assertEquals(test4.GetAllRefEnum2.A, anno.getA());
+ assertEquals(StringBuilder.class, anno.getC());
+ }
+
+ public void testGetAllRefB() throws Exception {
+ CtClass cc = sloader.get("test4.GetAllRefB");
+ ClassMap map = new ClassMap();
+ map.put("test4.GetAllRefAnno", "test4.GetAllRefAnno2");
+ map.put("test4.GetAllRefEnum", "test4.GetAllRefEnum2");
+ map.put("java.lang.String", "java.lang.StringBuilder");
+ cc.replaceClassName(map);
+ //cc.replaceClassName("test4.GetAllRefAnno", "test4.GetAllRefAnno2");
+ cc.writeFile();
+
+ cc = dloader.get(cc.getName());
+ test4.GetAllRefAnno2 anno
+ = (test4.GetAllRefAnno2)cc.getAnnotation(test4.GetAllRefAnno2.class);
+ assertEquals(test4.GetAllRefEnum2.A, anno.getA());
+ assertEquals(StringBuilder.class, anno.getC());
+ /*
+ AnnotationsAttribute aainfo = (AnnotationsAttribute)
+ cc.getClassFile().getAttribute(AnnotationsAttribute.visibleTag);
+ Annotation[] a = aainfo.getAnnotations();
+ System.err.println(a[0].getTypeName());
+ System.err.println(a[0]);
+ */
+ }
+
+ public void testGetAllRefC() throws Exception {
+ CtClass cc = sloader.get("test4.GetAllRefC");
+ HashSet set = new HashSet();
+ set.add("java.lang.Object");
+ set.add("java.lang.String");
+ set.add("test4.GetAllRefC");
+ set.add("test4.GetAllRefAnno");
+ set.add("test4.GetAllRefEnum");
+ set.add("test4.GetAllRefAnnoC");
+ set.add("test4.GetAllRefAnnoC2");
+ set.add("test4.GetAllRefAnnoC3");
+ set.add("test4.GetAllRefAnnoC4");
+ java.util.Collection<String> refs
+ = (java.util.Collection<String>)cc.getRefClasses();
+ assertEquals(set.size(), refs.size());
+ for (String s: refs) {
+ assertTrue(set.contains(s));
+ }
+ }
+
+ public void testGetAllRefInner() throws Exception {
+ HashSet set = new HashSet();
+ set.add("java.lang.Object");
+ set.add("test4.GetAllRefInnerTest");
+ set.add("test4.GetAllRefInnerTest$1");
+ set.add("test4.GetAllRefInnerTest$2");
+ CtClass cc = sloader.get("test4.GetAllRefInnerTest");
+ int size = 0;
+ for (Object s: cc.getRefClasses()) {
+ assertTrue((String)s, set.contains(s));
+ ++size;
+ }
+
+ assertEquals(set.size(), size);
+ }
+
+ public void testNestedClass() throws Exception {
+ CtClass cc = sloader.get("test4.NestedClass$1");
+ CtClass[] tab = cc.getNestedClasses();
+ assertEquals(1, tab.length);
+ assertEquals("test4.NestedClass$1$1", tab[0].getName());
+
+ cc = sloader.get("test4.NestedClass$1$1");
+ tab = cc.getNestedClasses();
+ assertEquals(0, tab.length);
+
+ cc = sloader.get("test4.NestedClass");
+ tab = cc.getNestedClasses();
+ for (CtClass c: tab) {
+ System.err.println(c.getName());
+ }
+
+ // Eclipse compiler sets tab.length to 4 but javac sets to 3.
+ assertTrue(tab.length == 4 || tab.length == 3);
+ for (CtClass c: tab) {
+ String name = c.getName();
+ assertTrue(name.equals("test4.NestedClass$N")
+ || name.equals("test4.NestedClass$S")
+ || name.equals("test4.NestedClass$1")
+ || name.equals("test4.NestedClass$1In"));
+ }
+
+ cc = sloader.get("test4.NestedClass$1In");
+ tab = cc.getNestedClasses();
+ assertEquals(0, tab.length);
+ }
+
+ public void testGetClasses() throws Exception {
+ CtClass cc = sloader.get("test4.NestedClass");
+ CtClass[] tab = cc.getDeclaredClasses();
+
+ // Eclipse compiler sets tab.length to 4 but javac sets to 3.
+ assertTrue(tab.length == 4 || tab.length == 3);
+ for (CtClass c: tab) {
+ String name = c.getName();
+ assertTrue(name.equals("test4.NestedClass$N")
+ || name.equals("test4.NestedClass$S")
+ || name.equals("test4.NestedClass$1")
+ || name.equals("test4.NestedClass$1In"));
+ }
+
+ cc = sloader.get("test4.NestedClass$1In");
+ tab = cc.getDeclaredClasses();
+ assertEquals(0, tab.length);
+ }
+
+ public void testImportPac() throws Exception {
+ CtClass cc = sloader.makeClass("test4.TestImpP");
+ sloader.importPackage("test4.NewImportPac");
+ try {
+ cc.addMethod(CtNewMethod.make(
+ "public int foo(){ " +
+ " ImportPac obj = new ImportPac();" +
+ " return obj.getClass().getName().length(); }", cc));
+ fail("ImportPac was found");
+ }
+ catch (CannotCompileException e) {}
+
+ cc.addMethod(CtNewMethod.make(
+ "public int bar(){ " +
+ " NewImportPac obj = new NewImportPac();" +
+ " return obj.getClass().getName().length(); }", cc));
+ sloader.clearImportedPackages();
+ }
+
+ public void testLength() throws Exception {
+ CtClass cc = sloader.makeClass("test4.LengthTest");
+ cc.addMethod(CtNewMethod.make(
+ "public int len(String s){ " +
+ " return s.length(); }", cc));
+ cc.addField(CtField.make("int length = 100;", cc));
+ cc.addConstructor(CtNewConstructor.defaultConstructor(cc));
+ cc.addMethod(CtNewMethod.make(
+ "public int run(){ " +
+ " test4.LengthTest t = new test4.LengthTest();" +
+ " return len(\"foo\") + t.length + test4.length.m(); }", cc));
+ try {
+ cc.addMethod(CtNewMethod.make(
+ "public int run(){ " +
+ " return test4no.length.m(); }", cc));
+ fail("test4no was found!");
+ }
+ catch (CannotCompileException e) {
+ System.out.println(e);
+ }
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(110, invoke(obj, "run"));
+ }
+
+ public void testAaload() throws Exception {
+ CtClass clazz = sloader.get("test4.Aaload");
+ CtMethod method = clazz.getMethod("narf", "()V");
+ method.instrument(new ExprEditor() {
+ @Override
+ public void edit(MethodCall call) throws CannotCompileException {
+ String name = call.getMethodName();
+ if (name.equals("addActionListener"))
+ call.replace("$0." + name + "($$);");
+ }
+ });
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testMakePackage() throws Exception {
+ if (ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9) {
+ ClassPool pool = ClassPool.getDefault();
+ try {
+ pool.makePackage(pool.getClassLoader(), "test4.pack2");
+ fail("makePackage() ran");
+ }
+ catch (CannotCompileException e) {}
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testPackage() throws Throwable { // JASSIST-147
+ String packageName = "test4.pack";
+ ClassPool pool = ClassPool.getDefault();
+ try {
+ pool.makePackage(pool.getClassLoader(), packageName);
+ pool.makePackage(pool.getClassLoader(), packageName);
+ }
+ catch (CannotCompileException e) {
+ if (ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9)
+ return; // makePackage() does not work in Java 9.
+ }
+
+ CtClass ctcl = pool.makeClass("test4.pack.Clazz");
+ Class cl = ctcl.toClass();
+ Object obj = cl.getConstructor().newInstance();
+ assertEquals(packageName, obj.getClass().getPackage().getName());
+ }
+
+ public static final String BASE_PATH = "../../";
+ public static final String JAVASSIST_JAR = BASE_PATH + "javassist.jar";
+ public static final String CLASSES_FOLDER = BASE_PATH + "build/classes";
+ public static final String TEST_CLASSES_FOLDER = BASE_PATH + "build/test-classes";
+
+ public static class Inner1 {
+ public static int get() {
+ return 0;
+ }
+ }
+
+ public void testJIRA150() throws Exception {
+ ClassPool pool = new ClassPool(true);
+ for(int paths=0; paths<50; paths++) {
+ pool.appendClassPath(JAVASSIST_JAR);
+ pool.appendClassPath(CLASSES_FOLDER);
+ pool.appendClassPath(TEST_CLASSES_FOLDER);
+ }
+ CtClass cc = pool.get("Jassist150$Inner1");
+ CtMethod ccGet = cc.getDeclaredMethod("get");
+ long startTime = System.currentTimeMillis();
+ for(int replace=0; replace<1000; replace++) {
+ ccGet.setBody(
+ "{ int n1 = java.lang.Integer#valueOf(1); " +
+ " int n2 = java.lang.Integer#valueOf(2); " +
+ " int n3 = java.lang.Integer#valueOf(3); " +
+ " int n4 = java.lang.Integer#valueOf(4); " +
+ " int n5 = java.lang.Integer#valueOf(5); " +
+ " return n1+n2+n3+n4+n5; }");
+ }
+ long endTime = System.currentTimeMillis();
+ for(int replace=0; replace<1000; replace++) {
+ ccGet.setBody(
+ "{ int n1 = java.lang.Integer.valueOf(1); " +
+ " int n2 = java.lang.Integer.valueOf(2); " +
+ " int n3 = java.lang.Integer.valueOf(3); " +
+ " int n4 = java.lang.Integer.valueOf(4); " +
+ " int n5 = java.lang.Integer.valueOf(5); " +
+ " return n1+n2+n3+n4+n5; }");
+ }
+ long endTime2 = System.currentTimeMillis();
+ for(int replace=0; replace<1000; replace++) {
+ ccGet.setBody(
+ "{ int n1 = Integer.valueOf(1); " +
+ " int n2 = Integer.valueOf(2); " +
+ " int n3 = Integer.valueOf(3); " +
+ " int n4 = Integer.valueOf(4); " +
+ " int n5 = Integer.valueOf(5); " +
+ " return n1+n2+n3+n4+n5; }");
+ }
+ long endTime3 = System.currentTimeMillis();
+ long t1 = endTime - startTime;
+ long t2 = endTime2 - endTime;
+ long t3 = endTime3 - endTime2;
+ System.out.println("JIRA150: " + t1 + ", " + t2 + ", " + t3);
+ assertTrue("performance test (the next try may succeed): " + t2 + " < 6 * " + t1,
+ t2 < t1 * 6);
+ assertTrue(t3 + " < 3 * " + t1, t3 < t1 * 3);
+ }
+
+ public void testJIRA150b() throws Exception {
+ int origSize = javassist.compiler.MemberResolver.getInvalidMapSize();
+ int N = 100;
+ for (int k = 0; k < N; k++) {
+ ClassPool pool = new ClassPool(true);
+ for(int paths=0; paths<50; paths++) {
+ pool.appendClassPath(JAVASSIST_JAR);
+ pool.appendClassPath(CLASSES_FOLDER);
+ pool.appendClassPath(TEST_CLASSES_FOLDER);
+ }
+ CtClass cc = pool.get("Jassist150$Inner1");
+ CtMethod ccGet = cc.getDeclaredMethod("get");
+ for(int replace=0; replace < 5; replace++) {
+ ccGet.setBody(
+ "{ int n1 = java.lang.Integer#valueOf(1); " +
+ " int n2 = java.lang.Integer#valueOf(2); " +
+ " int n3 = java.lang.Integer#valueOf(3); " +
+ " int n4 = java.lang.Integer#valueOf(4); " +
+ " int n5 = java.lang.Integer#valueOf(5); " +
+ " return n1+n2+n3+n4+n5; }");
+ }
+ pool = null;
+ }
+
+ // try to run garbage collection.
+ int[][] mem = new int[100][];
+ int[] large;
+ for (int i = 0; i < 100; i++) {
+ large = new int[1000000];
+ large[large.length - 2] = 9 + i;
+ mem[i] = large;
+ }
+ System.gc();
+ System.gc();
+ int size = javassist.compiler.MemberResolver.getInvalidMapSize();
+ System.out.println("JIRA150b " + size + " " + mem[mem.length - 1][mem[0].length - 2]);
+ // Now this seems obsolete.
+ // assertTrue("JIRA150b size: " + origSize + " " + size, size < origSize + N);
+ }
+
+ public void testJIRA152() throws Exception {
+ CtClass cc = sloader.get("test4.JIRA152");
+ CtMethod mth = cc.getDeclaredMethod("buildColumnOverride");
+ //CtMethod mth = cc.getDeclaredMethod("tested");
+ mth.instrument(new ExprEditor() {
+ public void edit(MethodCall c) throws CannotCompileException {
+ c.replace("try{ $_ = $proceed($$); } catch (Throwable t) { throw t; }");
+ }
+ });
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "test"));
+ }
+
+ public void testJIRA151() {
+ // try it using classloader of TestDescForName Desc.useContextClassLoader = false;
+ assertTrue(javassist.runtime.Desc.getClazz("[Ljava.lang.String;") != null);
+ //Thread.currentThread().setContextClassLoader(TestDescForName.class.getClassLoader());
+ boolean old = javassist.runtime.Desc.useContextClassLoader;
+ javassist.runtime.Desc.useContextClassLoader = true;
+ assertTrue(javassist.runtime.Desc.getClazz("[Ljava.lang.String;") != null);
+ javassist.runtime.Desc.useContextClassLoader = old;
+ }
+
+ public void testJIRA166() throws Exception {
+ CtClass cc = sloader.get("test4.JIRA166");
+ cc.instrument(new ExprEditor() {
+ public void edit(FieldAccess fa) throws CannotCompileException {
+ if (fa.isReader() && fa.getFieldName().equals("length"))
+ fa.replace("length = self().length + \"!\"; $_ = ($r) this.length.substring(1);");
+ }
+ });
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "run"));
+ }
+
+ public void testGenericSignature() throws Exception {
+ CtClass cc = sloader.makeClass("test4.GenSig");
+ CtClass objClass = sloader.get(CtClass.javaLangObject);
+ SignatureAttribute.ClassSignature cs
+ = new SignatureAttribute.ClassSignature(
+ new SignatureAttribute.TypeParameter[] {
+ new SignatureAttribute.TypeParameter("T") });
+ cc.setGenericSignature(cs.encode()); // <T:Ljava/lang/Object;>Ljava/lang/Object;
+
+ CtField f = new CtField(objClass, "value", cc);
+ SignatureAttribute.TypeVariable tvar = new SignatureAttribute.TypeVariable("T");
+ f.setGenericSignature(tvar.encode()); // TT;
+ cc.addField(f);
+
+ CtMethod m = CtNewMethod.make("public Object get(){return value;}", cc);
+ SignatureAttribute.MethodSignature ms
+ = new SignatureAttribute.MethodSignature(null, null, tvar, null);
+ m.setGenericSignature(ms.encode()); // ()TT;
+ cc.addMethod(m);
+
+ CtMethod m2 = CtNewMethod.make("public void set(Object v){value = v;}", cc);
+ SignatureAttribute.MethodSignature ms2
+ = new SignatureAttribute.MethodSignature(null, new SignatureAttribute.Type[] { tvar },
+ new SignatureAttribute.BaseType("void"), null);
+ m2.setGenericSignature(ms2.encode()); // (TT;)V;
+ cc.addMethod(m2);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ Class clazz = obj.getClass();
+ assertEquals("T", clazz.getTypeParameters()[0].getName());
+ assertEquals("T", ((java.lang.reflect.TypeVariable)clazz.getDeclaredField("value").getGenericType()).getName());
+ java.lang.reflect.Method rm = clazz.getDeclaredMethod("get", new Class[0]);
+ assertEquals("T", ((java.lang.reflect.TypeVariable)rm.getGenericReturnType()).getName());
+ java.lang.reflect.Method rm2 = clazz.getDeclaredMethod("set", new Class[] { Object.class });
+ assertEquals("T", ((java.lang.reflect.TypeVariable)rm2.getGenericParameterTypes()[0]).getName());
+ }
+
+ public void testJIRA171() throws Exception {
+ SignatureAttribute.MethodSignature ms
+ = SignatureAttribute.toMethodSignature("(Ljava/lang/Object;Lorg/apache/hadoop/io/Text;"
+ + "Lorg/apache/hadoop/mapreduce/Mapper<Ljava/lang/Object;Lorg/apache/hadoop/io/Text;"
+ + "Lorg/apache/hadoop/io/Text;Lorg/apache/hadoop/io/IntWritable;>.Context;)V");
+ String s = ms.toString();
+ System.out.println(s);
+ assertEquals("<> (java.lang.Object, org.apache.hadoop.io.Text, "
+ + "org.apache.hadoop.mapreduce.Mapper<java.lang.Object, org.apache.hadoop.io.Text, "
+ + "org.apache.hadoop.io.Text, org.apache.hadoop.io.IntWritable>.Context) void", s);
+ }
+
+ public void testAfter() throws Exception {
+ CtClass cc = sloader.get("test4.AfterTest");
+ CtMethod m1 = cc.getDeclaredMethod("m1");
+ m1.insertAfter("print();");
+ CtMethod m2 = cc.getDeclaredMethod("m2");
+ m2.insertAfter("print();");
+ CtMethod m3 = cc.getDeclaredMethod("m3");
+ m3.insertAfter("print();");
+ CtMethod m4 = cc.getDeclaredMethod("m4");
+ m4.insertAfter("print();");
+
+ CtMethod mm1 = cc.getDeclaredMethod("mm1");
+ mm1.insertAfter("print();", true);
+ CtMethod mm2 = cc.getDeclaredMethod("mm2");
+ mm2.insertAfter("print();", true);
+ CtMethod mm3 = cc.getDeclaredMethod("mm3");
+ mm3.insertAfter("print();", true);
+ CtMethod mm4 = cc.getDeclaredMethod("mm4");
+ mm4.insertAfter("print();", true);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(131, invoke(obj, "test1"));
+ assertEquals(112, invoke(obj, "test2"));
+ assertEquals(10, invoke(obj, "test3"));
+ assertEquals(100, invoke(obj, "test4"));
+
+ assertEquals(131, invoke(obj, "test11"));
+ assertEquals(112, invoke(obj, "test22"));
+ assertEquals(10, invoke(obj, "test33"));
+ assertEquals(100, invoke(obj, "test44"));
+ }
+
+ public void testJIRA186() throws Exception {
+ CtClass cc = sloader.get("test4.JIRA186");
+ cc.getDeclaredMethod("test").insertBefore("{" +
+ " java.util.List l = new java.util.ArrayList();" +
+ " l.add(this.toString());" +
+ "}");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "test"));
+ }
+
+ // JASSIST-190
+ public void testMultipleCatch() throws Exception {
+ CtClass cc = sloader.get("test4.MultiCatch");
+ CtMethod m1 = cc.getDeclaredMethod("m1");
+ m1.insertAfter("print();");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(12, invoke(obj, "test1"));
+ }
+
+ // JASSIST-185
+ public void testLocalVariableTypeTable() throws Exception {
+ CtClass cc = sloader.get("test4.Lvtt");
+ CtMethod m = cc.getDeclaredMethod("run");
+ m.addParameter(CtClass.intType);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ }
+
+ // JASSISt-181
+ public void testAnnotationWithGenerics() throws Exception {
+ CtClass cc0 = sloader.get("test4.JIRA181b");
+ CtField field0 = cc0.getField("aField");
+ String s0 = field0.getAnnotation(test4.JIRA181b.Condition.class).toString();
+ assertEquals("@test4.JIRA181b$Condition(condition=java.lang.String.class)", s0);
+ CtField field01 = cc0.getField("aField2");
+ String s01 = field01.getAnnotation(test4.JIRA181b.Condition.class).toString();
+ assertEquals("@test4.JIRA181b$Condition(condition=void.class)", s01);
+
+ CtClass cc = sloader.get("test4.JIRA181");
+ CtField field = cc.getField("aField");
+ String s = field.getAnnotation(test4.JIRA181.Condition.class).toString();
+ if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_9)
+ assertEquals("@test4.JIRA181$Condition(condition=test4.JIRA181<T>.B.class)", s);
+ else
+ assertEquals("@test4.JIRA181$Condition(condition=test4.JIRA181.B.class)", s);
+
+ CtField field2 = cc.getField("aField2");
+ String s2 = field2.getAnnotation(test4.JIRA181.Condition2.class).toString();
+ if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_9)
+ assertEquals("@test4.JIRA181$Condition2(condition=test4.JIRA181<T>.B[].class)", s2);
+ else
+ assertEquals("@test4.JIRA181$Condition2(condition=test4.JIRA181.B[].class)", s2);
+ }
+
+ public void testJIRA195() throws Exception {
+ CtClass cc = sloader.get("test4.JIRA195");
+ CtMethod mth = cc.getDeclaredMethod("test");
+ mth.getMethodInfo().rebuildStackMap(cc.getClassPool());
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(4, invoke(obj, "run"));
+ }
+
+ public void testJIRA188() throws Exception {
+ CtClass cc = sloader.makeClass("test4.JIRA188");
+ CtField f = new CtField(CtClass.intType, "f", cc);
+ f.setModifiers(Modifier.PRIVATE);
+ cc.addField(f);
+ cc.addMethod(CtNewMethod.make(
+ "public int getf(test4.JIRA188 p){ return p.f; }", cc));
+ cc.detach();
+ // System.gc();
+ try {
+ cc = sloader.get("test4.JIRA188");
+ fail("test4.JIRA188 found");
+ }
+ catch (NotFoundException e) {}
+ cc = sloader.makeClass("test4.JIRA188");
+ cc.addField(new CtField(CtClass.intType, "g", cc));
+ cc.addMethod(CtNewMethod.make(
+ "public int getf(test4.JIRA188 p){ return p.g; }", cc));
+ }
+
+ public void testJIRA158() throws Exception {
+ CtClass cc = sloader.get("test4.JIRA158");
+ cc.addMethod(CtMethod.make("public int run() { return obj.foo(jj, dd) + obj.bar(j, d); }", cc));
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(15, invoke(obj, "run"));
+ }
+
+ public void testJIRA207() throws Exception {
+ CtClass cc = sloader.get("test4.JIRA207");
+ CtMethod cm = cc.getDeclaredMethod("foo");
+ cm.insertBefore("throw new Exception();");
+
+ CtMethod cm2 = cc.getDeclaredMethod("run2");
+ cm2.insertBefore("throw new Exception();");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ try {
+ invoke(obj, "run2");
+ fail("run2");
+ }
+ catch (Exception e) {}
+ }
+
+ public void testJIRA212() throws Exception {
+ CtClass cc = sloader.get("test4.JIRA212");
+ for (final CtBehavior behavior : cc.getDeclaredBehaviors()) {
+ behavior.instrument(new ExprEditor() {
+ public void edit(FieldAccess fieldAccess) throws CannotCompileException {
+ String fieldName = fieldAccess.getFieldName().substring(0,1).toUpperCase() + fieldAccess.getFieldName().substring(1);
+ if (fieldAccess.isReader()) {
+ // Rewrite read access
+ fieldAccess.replace("$_ = $0.get" + fieldName + "();");
+ } else if (fieldAccess.isWriter()) {
+ // Rewrite write access
+ fieldAccess.replace("$0.set" + fieldName + "($1);");
+ }
+ }});
+ }
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ }
+
+ public void testWhileTrueKO() throws Exception {
+ final ClassPool pool = ClassPool.getDefault();
+ final CtClass cc = pool.makeClass("test4.TestWhileTrueKO");
+ String source = "public void testWhile() { while(true) { break; } }";
+ cc.addMethod(CtMethod.make(source, cc));
+ cc.writeFile();
+ make(cc.getName());
+ }
+
+ public void testWhileTrueOK() throws Exception {
+ final ClassPool pool = ClassPool.getDefault();
+ final CtClass cc = pool.makeClass("test4.TestWhileTrueOK");
+ String source = "public void testWhile() { while(0==0) { break; }}";
+ cc.addMethod(CtMethod.make(source, cc));
+ cc.writeFile();
+ make(cc.getName());
+ }
+
+ // JIRA JASSIST-224
+ public void testMethodParameters() throws Exception {
+ Class rc = test4.MethodParamTest.class;
+ java.lang.reflect.Method m = rc.getDeclaredMethods()[0];
+ java.lang.reflect.Parameter[] params = m.getParameters();
+ assertEquals("i", params[0].getName());
+ assertEquals("s", params[1].getName());
+
+ CtClass cc = sloader.get("test4.MethodParamTest");
+ ClassFile cf = cc.getClassFile2();
+ ConstPool cp = cf.getConstPool();
+ MethodInfo minfo = cf.getMethod("test");
+ MethodParametersAttribute attr
+ = (MethodParametersAttribute)minfo.getAttribute(MethodParametersAttribute.tag);
+ assertEquals(2, attr.size());
+ assertEquals("i", cp.getUtf8Info(attr.name(0)));
+ assertEquals("s", cp.getUtf8Info(attr.name(1)));
+
+ attr = (MethodParametersAttribute)attr.copy(cp, null);
+ assertEquals(2, attr.size());
+ assertEquals("i", cp.getUtf8Info(attr.name(0)));
+ assertEquals("s", cp.getUtf8Info(attr.name(1)));
+ }
+
+ // JIRA JASSIST-220
+ public void testStaticInterfaceMethods() throws Exception {
+ CtClass cc = sloader.get("test4.JIRA220");
+
+ cc.getMethod("foo", "()V").instrument(new ExprEditor() {
+ @Override
+ public void edit(MethodCall m) throws CannotCompileException {
+ try {
+ m.getClassName();
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+ });
+ }
+
+ // JIRA-230
+ public void testDeadcode() throws Exception {
+ CtClass newClass = sloader.makeClass("test4.TestDeadcode");
+ addDeadCode(newClass, "public void evaluate(){if (false) {int i = 0;}}");
+ addDeadCode(newClass, "public void evaluate2(){if (false == true) {int i = 0;}}");
+ addDeadCode(newClass, "public void evaluate3(){if (true) {} else {} int i = 0; if (false) {} else {} i++; }");
+ addDeadCode(newClass, "public void evaluate4(){ for (;false;); int i = false ? 1 : 0; while (true) { return; }}");
+ addDeadCode(newClass, "public void evaluate5(){ boolean b = !false; b = false && b; b = true && true;"
+ + " b = true || b; b = b || false; }");
+ addDeadCode(newClass, "public boolean evaluate6(){ return !false; }");
+ addDeadCode(newClass, "public boolean evaluate7(){ return !true; }");
+
+ newClass.debugWriteFile();
+ Class<?> cClass = newClass.toClass(test4.DefineClassCapability.class);
+ Object o = cClass.getConstructor().newInstance();
+ java.lang.reflect.Method m = cClass.getMethod("evaluate");
+ m.invoke(o);
+ m = cClass.getMethod("evaluate2");
+ m.invoke(o);
+ m = cClass.getMethod("evaluate3");
+ m.invoke(o);
+ m = cClass.getMethod("evaluate4");
+ m.invoke(o);
+ m = cClass.getMethod("evaluate6");
+ assertTrue((boolean)m.invoke(o));
+ m = cClass.getMethod("evaluate7");
+ assertFalse((boolean)m.invoke(o));
+ m = cClass.getMethod("evaluate5");
+ m.invoke(o);
+ }
+
+ private void addDeadCode(CtClass cc, String meth) throws Exception {
+ CtMethod m = CtNewMethod.make(meth, cc);
+ cc.addMethod(m);
+ }
+
+ public void testAnnArg() throws Exception {
+ CtClass cc = sloader.get("test4.AnnoArg");
+ CtMethod m = cc.getDeclaredMethod("foo");
+ test4.AnnoArg.AnnoArgAt a = (test4.AnnoArg.AnnoArgAt)m.getAnnotations()[0];
+ assertEquals("test4.AnnoArg$B", a.value().getName());
+ System.out.println(a.value().getName());
+ }
+
+ public void testDeclaredMethods() throws Exception {
+ CtClass cc = sloader.get("test4.DeclMethodsList");
+ CtMethod[] meth = cc.getDeclaredMethods("foo");
+ assertEquals(2, meth.length);
+ assertEquals("()V", meth[0].getSignature());
+ assertEquals("(I)I", meth[1].getSignature());
+ meth = cc.getDeclaredMethods("bar");
+ assertEquals(1, meth.length);
+ assertEquals("()V", meth[0].getSignature());
+ meth = cc.getDeclaredMethods("baz");
+ assertEquals(0, meth.length);
+ }
+
+ public void testAnnotationLoader() throws Exception {
+ CtClass anno = sloader.makeAnnotation("test4.AnnoLoadAnno");
+ anno.debugWriteFile();
+ CtClass cc = sloader.get("test4.AnnoLoad");
+ CtMethod m = cc.getDeclaredMethod("foo");
+ ClassFile cf = cc.getClassFile();
+ ConstPool cp = cf.getConstPool();
+ AnnotationsAttribute attr
+ = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
+ Annotation a = new Annotation(anno.getName(), cp);
+ a.addMemberValue("value", new javassist.bytecode.annotation.StringMemberValue("file/path", cp));
+ attr.setAnnotation(a);
+ m.getMethodInfo().addAttribute(attr);
+ cc.writeFile();
+ anno.toClass(test4.DefineClassCapability.class);
+ Class<?> rc = ((java.lang.annotation.Annotation)m.getAnnotations()[0]).annotationType();
+ assertEquals(anno.getName(), rc.getName());
+ }
+}
diff --git a/src/test/javassist/JvstTest5.java b/src/test/javassist/JvstTest5.java
new file mode 100644
index 0000000..c5eff4d
--- /dev/null
+++ b/src/test/javassist/JvstTest5.java
@@ -0,0 +1,456 @@
+package javassist;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.TypeVariable;
+
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.AnnotationsAttribute;
+import javassist.bytecode.AttributeInfo;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.InnerClassesAttribute;
+import javassist.bytecode.NestHostAttribute;
+import javassist.bytecode.NestMembersAttribute;
+import javassist.expr.ExprEditor;
+import javassist.expr.Handler;
+import javassist.expr.MethodCall;
+
+@SuppressWarnings({"rawtypes","unchecked","unused"})
+public class JvstTest5 extends JvstTestRoot {
+ public JvstTest5(String name) {
+ super(name);
+ }
+
+ public void testDollarClassInStaticMethod() throws Exception {
+ CtClass cc = sloader.makeClass("test5.DollarClass");
+ CtMethod m = CtNewMethod.make("public static int run(){ return $class.getName().length(); }", cc);
+ cc.addMethod(m);
+ m = CtNewMethod.make("public int run2(){ return $class.getName().length(); }", cc);
+ cc.addMethod(m);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(cc.getName().length(), invoke(obj, "run"));
+ assertEquals(cc.getName().length(), invoke(obj, "run2"));
+ }
+
+ public void testSuperDefaultMethodCall() throws Exception {
+ CtClass cc = sloader.get("test5.DefaultMethod");
+ CtMethod m = CtNewMethod.make("public int run(){ return test5.DefaultMethodIntf.super.foo(); }", cc);
+ cc.addMethod(m);
+ m = CtNewMethod.make("public int run2(){ return test5.DefaultMethodIntf.baz(); }", cc);
+ cc.addMethod(m);
+ m = CtNewMethod.make("public int run3(){ return test5.DefaultMethodIntf.super.baz(); }", cc);
+ cc.addMethod(m);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "run"));
+ assertEquals(10, invoke(obj, "run2"));
+ assertEquals(10, invoke(obj, "run3"));
+ }
+
+ public void testTypeAnno() throws Exception {
+ CtClass cc = sloader.get("test5.TypeAnno");
+ cc.getClassFile().compact();
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ TypeVariable<?> t = obj.getClass().getTypeParameters()[0];
+ Annotation[] annos = t.getAnnotations();
+ assertEquals("@test5.TypeAnnoA()", annos[0].toString());
+ }
+
+ public void testJIRA241() throws Exception {
+ CtClass cc = sloader.get("test5.JIRA241");
+ CtMethod testMethod = cc.getDeclaredMethod("test");
+ testMethod.insertAfter("System.out.println(\"inserted!\");");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(10, invoke(obj, "run"));
+ }
+
+ public void testJIRA246() throws Exception {
+ CtClass ctClass = sloader.makeClass("test5.JIRA246Test");
+ ctClass.addInterface(sloader.get(test5.JIRA246.Test.class.getName()));
+ String methodBody = "public void test() { defaultMethod(); }";
+ CtMethod ctMethod = CtMethod.make(methodBody, ctClass);
+ ctClass.addMethod(ctMethod);
+ }
+
+ public void testJIRA246b() throws Exception {
+ CtClass ctClass = sloader.get(test5.JIRA246.A.class.getName());
+ String src = "public void id() { get(); }";
+ CtMethod make = CtNewMethod.make(src, ctClass);
+ }
+
+ public void testJIRA242() throws Exception {
+ Boolean ss = Boolean.valueOf(2 > 3);
+ ClassPool cp = ClassPool.getDefault();
+ CtClass cc = cp.get("test5.JIRA242$Hello");
+ CtMethod m = cc.getDeclaredMethod("say");
+ m.insertBefore("{ System.out.println(\"Say Hello...\"); }");
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("BOOL_SERIES = createBooleanSeriesStep();");
+ //Below code cause the issue
+ sb.append("BOOL_SERIES.setValue(3>=3);"); //lets comment this and run it will work
+ // Below code snippets will work
+ // this cast into exact class and call the same function
+ sb.append("((test5.JIRA242$BooleanDataSeries)BOOL_SERIES).setValue(3>=3);");
+ // this code snippet will set exact boolean variable to the function.
+ sb.append("boolean var = 3>=3;");
+ sb.append("BOOL_SERIES.setValue(var);");
+
+ m.insertBefore(sb.toString());
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(0, invoke(obj, "say"));
+ }
+
+ public void testJIRA249() throws Exception {
+ CtClass cc = sloader.get("test5.BoolTest");
+ CtMethod testMethod = cc.getDeclaredMethod("test");
+ testMethod.insertBefore("i = foo(true & true);");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "run"));
+ }
+
+ public void testInnerClassAttributeRemove() throws Exception {
+ CtClass cc = sloader.get("test5.InnerClassRemove");
+ ClassFile cf = cc.getClassFile();
+ InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute(InnerClassesAttribute.tag);
+ String second = ica.innerClass(1);
+ String secondName = ica.innerName(1);
+ String third = ica.innerClass(2);
+ String thirdName = ica.innerName(2);
+ assertEquals(3, ica.remove(3));
+ assertEquals(2, ica.remove(0));
+ assertEquals(second, ica.innerClass(0));
+ assertEquals(secondName, ica.innerName(0));
+ assertEquals(third, ica.innerClass(1));
+ assertEquals(thirdName, ica.innerName(1));
+ assertEquals(1, ica.remove(1));
+ assertEquals(second, ica.innerClass(0));
+ assertEquals(secondName, ica.innerName(0));
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "run"));
+ }
+
+ public void testJIRA248() throws Exception {
+ CtClass cc = sloader.get("test5.JIRA248");
+ String methodBody = "public int run() { return foo() + super.foo() + super.bar() + test5.JIRA248Intf2.super.baz(); }";
+ CtMethod ctMethod = CtMethod.make(methodBody, cc);
+ cc.addMethod(ctMethod);
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(40271, invoke(obj, "run"));
+ }
+
+ public void testInvalidCastWithDollar() throws Exception {
+ String code = "{ new test5.JavassistInvalidCastTest().inspectReturn((Object) ($w) $_); } ";
+ CtClass c = sloader.get("test5.InvalidCastDollar");
+ for (CtMethod method : c.getDeclaredMethods())
+ method.insertAfter(code);
+ }
+
+ public void testJIRA256() throws Exception {
+ // CtClass ec = sloader.get("test5.Entity");
+
+ CtClass cc = sloader.makeClass("test5.JIRA256");
+ ClassFile ccFile = cc.getClassFile();
+ ConstPool constpool = ccFile.getConstPool();
+
+ AnnotationsAttribute attr = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
+ javassist.bytecode.annotation.Annotation entityAnno
+ = new javassist.bytecode.annotation.Annotation("test5.Entity", constpool);
+ // = new javassist.bytecode.annotation.Annotation(constpool, ec);
+
+ entityAnno.addMemberValue("value", new javassist.bytecode.annotation.ArrayMemberValue(constpool));
+ attr.addAnnotation(entityAnno);
+ ccFile.addAttribute(attr);
+
+ cc.writeFile();
+ Object o = make(cc.getName());
+ assertTrue(o.getClass().getName().equals("test5.JIRA256"));
+
+ java.lang.annotation.Annotation[] annotations = o.getClass().getDeclaredAnnotations();
+ assertEquals(1, annotations.length);
+ }
+
+ public void testJIRA250() throws Exception {
+ CtClass cc = sloader.makeClass("test5.JIRA250", sloader.get("test5.JIRA250Super"));
+ cc.addMethod(CtNewMethod.make(
+ " public test5.JIRA250Bar getBar() {" +
+ " return super.getBar();\n" +
+ " }\n", cc));
+ cc.addMethod(CtNewMethod.make("public int run() { getBar(); return 1; }", cc));
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "run"));
+ }
+
+ public void testProceedToDefaultMethod() throws Exception {
+ CtClass cc = ClassPool.getDefault().get("test5.ProceedDefault");
+ CtMethod mth = cc.getDeclaredMethod("bar");
+ mth.instrument(new ExprEditor() {
+ public void edit(MethodCall c) throws CannotCompileException {
+ c.replace("$_ = $proceed($$) + 10000;");
+ }
+ });
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(21713, invoke(obj, "run"));
+ }
+
+ public void testBadClass() throws Exception {
+ CtClass badClass = ClassPool.getDefault().makeClass("badClass");
+ String src = String.join(System.getProperty("line.separator"),
+ "public void eval () {",
+ " if (true) {",
+ " double t=0;",
+ " } else {",
+ " double t=0;",
+ " }",
+ " for (int i=0; i < 2; i++) {",
+ " int a=0;",
+ " int b=0;",
+ " int c=0;",
+ " int d=0;",
+ " if (true) {",
+ " int e = 0;",
+ " }",
+ " }",
+ "}");
+ System.out.println(src);
+ badClass.addMethod(CtMethod.make(src, badClass));
+ Class clazzz = badClass.toClass(Class.forName("DefineClassCapability"));
+ Object obj = clazzz.getConstructor().newInstance(); // <-- falls here
+ }
+
+ public void test83StackmapWithArrayType() throws Exception {
+ final CtClass ctClass = sloader.get("test5.StackmapWithArray83");
+ final CtMethod method = ctClass.getDeclaredMethod("bytecodeVerifyError");
+ method.addLocalVariable("test_localVariable", CtClass.intType);
+ method.insertBefore("{ test_localVariable = 1; }");
+
+ final CtMethod method2 = ctClass.getDeclaredMethod("bytecodeVerifyError2");
+ method2.addLocalVariable("test_localVariable", CtClass.intType);
+ method2.insertBefore("{ test_localVariable = 1; }");
+
+ ctClass.writeFile();
+ Object obj = make(ctClass.getName());
+ assertEquals(1, invoke(obj, "run"));
+ }
+
+ public void testLoaderClassPath() throws Exception {
+ ClassPool cp = new ClassPool();
+ cp.appendClassPath(new LoaderClassPath(new Loader()));
+ assertNotNull(cp.get(Object.class.getName()));
+ assertNotNull(cp.get(this.getClass().getName()));
+ }
+
+ public void testAddDefaultMethod() throws Exception {
+ CtClass cc = sloader.makeInterface("test5.AddDefaultMethod");
+ cc.addMethod(CtNewMethod.make("static int foo() { return 1; }", cc));
+ cc.addMethod(CtNewMethod.make("public static int foo1() { return 1; }", cc));
+ cc.addMethod(CtNewMethod.make("public int foo2() { return 1; }", cc));
+ cc.addMethod(CtNewMethod.make("int foo3() { return 1; }", cc));
+ try {
+ cc.addMethod(CtNewMethod.make("private int foo4() { return 1; }", cc));
+ fail();
+ } catch (CannotCompileException e) {}
+ try {
+ cc.addMethod(CtNewMethod.make("private static int foo5() { return 1; }", cc));
+ fail();
+ } catch (CannotCompileException e) {}
+ }
+
+ public void testRemoveAnnotatino() throws Exception {
+ CtClass cc = sloader.get("test5.RemoveAnnotation");
+ AnnotationsAttribute aa
+ = (AnnotationsAttribute)cc.getClassFile().getAttribute(AnnotationsAttribute.invisibleTag);
+ assertTrue(aa.removeAnnotation("test5.RemoveAnno1"));
+ AttributeInfo ai = cc.getClassFile().removeAttribute(AnnotationsAttribute.invisibleTag);
+ assertEquals(ai.getName(), AnnotationsAttribute.invisibleTag);
+
+ CtMethod foo = cc.getDeclaredMethod("foo");
+ AnnotationsAttribute aa2 = (AnnotationsAttribute)foo.getMethodInfo().getAttribute(AnnotationsAttribute.invisibleTag);
+ assertTrue(aa2.removeAnnotation("test5.RemoveAnno1"));
+
+ CtMethod bar = cc.getDeclaredMethod("bar");
+ AnnotationsAttribute aa3 = (AnnotationsAttribute)bar.getMethodInfo().getAttribute(AnnotationsAttribute.invisibleTag);
+ assertFalse(aa3.removeAnnotation("test5.RemoveAnno1"));
+ assertTrue(aa3.removeAnnotation("test5.RemoveAnno2"));
+ AttributeInfo ai2 = bar.getMethodInfo().removeAttribute(AnnotationsAttribute.invisibleTag);
+ assertEquals(ai2.getName(), AnnotationsAttribute.invisibleTag);
+
+ CtMethod run = cc.getDeclaredMethod("run");
+ AttributeInfo ai3 = run.getMethodInfo().removeAttribute(AnnotationsAttribute.invisibleTag);
+ assertNull(ai3);
+
+ CtField baz = cc.getDeclaredField("baz");
+ AttributeInfo ai4 = baz.getFieldInfo().removeAttribute(AnnotationsAttribute.invisibleTag);
+ assertEquals(ai4.getName(), AnnotationsAttribute.invisibleTag);
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(3, invoke(obj, "run"));
+ }
+
+ public void testInnerClassModifiers() throws Exception {
+ CtClass cc = sloader.get("test5.InnerModifier$NonStatic");
+ try {
+ cc.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
+ fail();
+ }
+ catch (RuntimeException e) {
+ if (!e.getMessage().startsWith("cannot change "))
+ fail();
+ }
+
+ cc.setModifiers(Modifier.PUBLIC);
+ cc.writeFile();
+
+ assertEquals(Modifier.PUBLIC, cc.getModifiers());
+ InnerClassesAttribute ica = getInnerClassAttr(cc);
+ int i = ica.find("test5.InnerModifier$NonStatic");
+ assertTrue(i >= 0);
+ assertEquals(Modifier.PUBLIC, ica.accessFlags(i));
+
+ CtClass cc2 = sloader.get("test5.InnerModifier$Static");
+
+ InnerClassesAttribute ica3 = getInnerClassAttr(cc2);
+ int i3 = ica3.find("test5.InnerModifier$Static");
+ assertTrue(i3 >= 0);
+ assertEquals(AccessFlag.STATIC, ica3.accessFlags(i3));
+
+ cc2.setModifiers(Modifier.PROTECTED | Modifier.STATIC);
+ cc2.setModifiers(Modifier.PUBLIC);
+ cc2.writeFile();
+
+ assertEquals(Modifier.PUBLIC | Modifier.STATIC, cc2.getModifiers());
+ InnerClassesAttribute ica2 = getInnerClassAttr(cc2);
+ int i2 = ica2.find("test5.InnerModifier$Static");
+ assertTrue(i2 >= 0);
+ assertEquals(AccessFlag.PUBLIC | AccessFlag.STATIC, ica2.accessFlags(i2));
+
+ CtClass cc3 = cc.getDeclaringClass();
+ assertTrue(cc3.isModified());
+ cc3.writeFile();
+
+ InnerClassesAttribute ica4 = getInnerClassAttr(cc3);
+ int i4 = ica4.find("test5.InnerModifier$Static");
+ assertTrue(i4 >= 0);
+ assertEquals(AccessFlag.PUBLIC | AccessFlag.STATIC, ica4.accessFlags(i4));
+ int i5 = ica4.find("test5.InnerModifier$NonStatic");
+ assertTrue(i5 >= 0);
+ assertEquals(Modifier.PUBLIC, ica4.accessFlags(i5));
+ }
+
+ public void testInnerClassModifiers2() throws Exception {
+ CtClass cc = sloader.get("test5.InnerModifier2$Protected");
+ Class<?> ccc = Class.forName("test5.InnerModifier2$Protected");
+ assertEquals(cc.getModifiers(), ccc.getModifiers());
+ assertTrue(Modifier.isProtected(cc.getModifiers()));
+
+ cc = sloader.get("test5.InnerModifier2$Public");
+ ccc = Class.forName("test5.InnerModifier2$Public");
+ assertEquals(cc.getModifiers(), ccc.getModifiers());
+ assertTrue(Modifier.isPublic(cc.getModifiers()));
+
+ cc = sloader.get("test5.InnerModifier2$Private");
+ ccc = Class.forName("test5.InnerModifier2$Private");
+ assertEquals(cc.getModifiers(), ccc.getModifiers());
+ assertTrue(Modifier.isPrivate(cc.getModifiers()));
+
+ cc = sloader.get("test5.InnerModifier2$Package");
+ ccc = Class.forName("test5.InnerModifier2$Package");
+ assertEquals(cc.getModifiers(), ccc.getModifiers());
+ assertTrue(Modifier.isPackage(cc.getModifiers()));
+
+ cc = sloader.get("test5.InnerModifier2$ProtectedStatic");
+ ccc = Class.forName("test5.InnerModifier2$ProtectedStatic");
+ assertEquals(cc.getModifiers(), ccc.getModifiers());
+ assertTrue(Modifier.isProtected(cc.getModifiers()));
+ assertTrue(Modifier.isStatic(cc.getModifiers()));
+
+ cc = sloader.get("test5.InnerModifier2$PublicStatic");
+ ccc = Class.forName("test5.InnerModifier2$PublicStatic");
+ assertEquals(cc.getModifiers(), ccc.getModifiers());
+ assertTrue(Modifier.isPublic(cc.getModifiers()));
+ assertTrue(Modifier.isStatic(cc.getModifiers()));
+
+ cc = sloader.get("test5.InnerModifier2$PrivateStatic");
+ ccc = Class.forName("test5.InnerModifier2$PrivateStatic");
+ assertEquals(cc.getModifiers(), ccc.getModifiers());
+ assertTrue(Modifier.isPrivate(cc.getModifiers()));
+ assertTrue(Modifier.isStatic(cc.getModifiers()));
+
+ cc = sloader.get("test5.InnerModifier2$PackageStatic");
+ ccc = Class.forName("test5.InnerModifier2$PackageStatic");
+ assertEquals(cc.getModifiers(), ccc.getModifiers());
+ assertTrue(Modifier.isPackage(cc.getModifiers()));
+ assertTrue(Modifier.isStatic(cc.getModifiers()));
+ }
+
+ private InnerClassesAttribute getInnerClassAttr(CtClass cc) {
+ return (InnerClassesAttribute)cc.getClassFile2().getAttribute(InnerClassesAttribute.tag);
+ }
+
+ public void testVarArgsModifier() throws Exception {
+ CtClass cc = sloader.get("test5.VarArgsMethod");
+ assertTrue(Modifier.isVarArgs(cc.getDeclaredMethod("foo").getModifiers()));
+ assertFalse(Modifier.isVarArgs(cc.getDeclaredMethod("bar").getModifiers()));
+ }
+
+ public void testIssue155() throws Exception {
+ CtClass cc = sloader.get("test5.Issue155");
+ CtMethod testMethod = cc.getDeclaredMethod("foo");
+ testMethod.instrument(
+ new ExprEditor() {
+ public void edit(Handler m)
+ throws CannotCompileException {
+ m.insertBefore("throw $1;");
+ }
+ });
+
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "test"));
+ }
+
+ public void testNestHostAttribute() throws Exception {
+ CtClass cc = sloader.get("test5.NestHost$Foo");
+ ClassFile cf = cc.getClassFile();
+ NestHostAttribute attr = (NestHostAttribute)cf.getAttribute(NestHostAttribute.tag);
+ assertEquals(test5.NestHost.class.getName(),
+ cf.getConstPool().getClassInfo(attr.hostClassIndex()));
+ }
+
+ public void testNestMembersAttribute() throws Exception {
+ CtClass cc = sloader.get("test5.NestHost");
+ ClassFile cf = cc.getClassFile();
+ NestMembersAttribute attr = (NestMembersAttribute)cf.getAttribute(NestMembersAttribute.tag);
+ assertEquals(2, attr.numberOfClasses());
+ String[] names = new String[2];
+ for (int i = 0; i < 2; i++)
+ names[i] = cf.getConstPool().getClassInfo(attr.memberClass(i));
+
+ assertFalse(names[0].equals(names[1]));
+ assertTrue(names[0].equals("test5.NestHost$Foo") || names[0].equals("test5.NestHost$Bar"));
+ assertTrue(names[1].equals("test5.NestHost$Foo") || names[1].equals("test5.NestHost$Bar"));
+ }
+
+ public void testNestMembersAttributeCopy() throws Exception {
+ CtClass cc = sloader.get("test5.NestHost2");
+ cc.getClassFile().compact();
+ cc.writeFile();
+ make(cc.getName());
+ }
+
+ public void testNestHostAttributeCopy() throws Exception {
+ CtClass cc = sloader.get("test5.NestHost2$Foo");
+ cc.getClassFile().compact();
+ cc.toClass(test5.DefineClassCapability.class);
+ }
+}
diff --git a/src/test/javassist/JvstTestRoot.java b/src/test/javassist/JvstTestRoot.java
new file mode 100644
index 0000000..69f4a6a
--- /dev/null
+++ b/src/test/javassist/JvstTestRoot.java
@@ -0,0 +1,53 @@
+package javassist;
+
+import junit.framework.*;
+import java.lang.reflect.Method;
+
+public class JvstTestRoot extends TestCase {
+ // the directory where all compiled class files are found.
+ public static final String PATH = "../../target/test-classes/";
+
+ // the directory where javassist.jar is found.
+ public static final String JAR_PATH = "../../";
+
+ ClassPool sloader, dloader;
+ Loader cloader;
+
+ public JvstTestRoot(String name) {
+ super(name);
+ }
+
+ protected void print(String msg) {
+ System.out.println(msg);
+ }
+
+ protected void print(Exception e) {
+ e.printStackTrace();
+ }
+
+ protected void setUp() throws Exception {
+ sloader = ClassPool.getDefault();
+ dloader = new ClassPool(null);
+ dloader.appendSystemPath();
+ dloader.insertClassPath(".");
+ cloader = new Loader(dloader);
+ }
+
+ protected Object make(String name) throws Exception {
+ return cloader.loadClass(name).getConstructor().newInstance();
+ }
+
+ protected int invoke(Object target, String method) throws Exception {
+ Method m = target.getClass().getMethod(method, new Class[0]);
+ Object res = m.invoke(target, new Object[0]);
+ return ((Integer)res).intValue();
+ }
+
+ protected int invoke(Object target, String method, int arg)
+ throws Exception {
+ Method m =
+ target.getClass().getMethod(method, new Class[] { int.class });
+ Object res = m.invoke(target, new Object[] { Integer.valueOf(arg)});
+ return ((Integer) res).intValue();
+ }
+}
diff --git a/src/test/javassist/LoaderTestByRandall.java b/src/test/javassist/LoaderTestByRandall.java
new file mode 100644
index 0000000..04e05cd
--- /dev/null
+++ b/src/test/javassist/LoaderTestByRandall.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) Brett Randall 2004. All rights reserved.
+ *
+ * Created on Jul 20, 2004
+ */
+
+package javassist;
+
+import junit.framework.TestCase;
+
+/**
+ * @author brandall
+ */
+@SuppressWarnings({"rawtypes","unused"})
+public class LoaderTestByRandall extends TestCase {
+
+ ClassPool cp;
+ Loader loader;
+
+ public LoaderTestByRandall(String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ cp = new ClassPool();
+ cp.appendSystemPath();
+ loader = new Loader(cp);
+ }
+
+ public void testLoadGoodClass() throws Exception {
+ String name = "javassist.LoaderTestByRandall";
+ cp.get(name);
+ Class clazz = loader.loadClass(name);
+ assertEquals("Class not loaded by loader",
+ loader, clazz.getClassLoader());
+ }
+
+ public void testLoadGoodClassByDelegation() throws Exception {
+ Class clazz = loader.loadClass("java.lang.String");
+ }
+
+ public void testLoadBadClass() {
+ try {
+ Class clazz = loader.loadClass("never.going.to.find.Class");
+ fail("Expected ClassNotFoundException to be thrown");
+ } catch (ClassNotFoundException e) {
+ // expected
+ }
+ }
+
+ public void testLoadBadClassByDelegation() {
+ try {
+ Class clazz = loader.loadClass("java.never.going.to.find.Class");
+ fail("Expected ClassNotFoundException to be thrown");
+ } catch (ClassNotFoundException e) {
+ // expected
+ }
+ }
+
+ public void testLoadBadCodeModification() throws Exception {
+ String classname = "javassist.LoaderTestByRandall";
+
+ Translator trans = new Translator() {
+ public void start(ClassPool pool)
+ throws NotFoundException, CannotCompileException
+ {
+ }
+
+ public void onLoad(ClassPool pool, String classname)
+ throws NotFoundException, CannotCompileException
+ {
+ String body = new String("this will never compile");
+ CtClass clazz = pool.get(classname);
+ CtMethod newMethod = CtNewMethod.make(CtClassType.voidType,
+ "wontCompileMethod",
+ new CtClass[] {},
+ new CtClass[] {},
+ body,
+ clazz);
+ clazz.addMethod(newMethod);
+ }
+ };
+
+ loader.addTranslator(cp, trans);
+
+ try {
+ Class clazz = loader.loadClass(classname);
+ fail("Expected loader to throw ClassNotFoundException " +
+ "caused by CannotCompileException");
+ } catch (ClassNotFoundException e) {
+ // expected
+ System.out.println(e);
+ assertEquals("ClassNotFoundException was not caused "
+ + "by CannotCompileException",
+ CannotCompileException.class,
+ e.getCause().getClass());
+ }
+ }
+}
diff --git a/src/test/javassist/SetterTest.java b/src/test/javassist/SetterTest.java
new file mode 100644
index 0000000..a76b2a3
--- /dev/null
+++ b/src/test/javassist/SetterTest.java
@@ -0,0 +1,114 @@
+package javassist;
+import java.lang.reflect.Method;
+
+import junit.framework.TestCase;
+
+@SuppressWarnings({"rawtypes","unchecked"})
+public class SetterTest extends TestCase {
+
+ ClassPool pool;
+ Class<?> capability;
+
+ public SetterTest(String name) {
+ super(name);
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ pool = ClassPool.getDefault();
+ capability = Class.forName("DefineClassCapability");
+ }
+
+ /**
+ * Tests a getter only on a field without a Modifier.
+ *
+ * @throws Exception
+ */
+ public void testFieldGetter() throws Exception {
+ CtClass clazz = pool.makeClass("HasFieldGetter");
+ clazz.setSuperclass(pool.get("java.lang.Object"));
+ CtField field = new CtField(CtClass.booleanType, "broken", clazz);
+ clazz.addField(field, "true");
+ clazz.addMethod(CtNewMethod.getter("isBroken", field));
+ Class _class = clazz.toClass(capability);
+
+ Object object = _class.getConstructor().newInstance();
+ check(_class, object, true);
+ }
+
+ /**
+ * Tests a getter and a setter on a field without a Modifier.
+ *
+ * @throws Exception
+ */
+ public void testFieldGetterSetter() throws Exception {
+ CtClass clazz = pool.makeClass("HasFieldGetterSetter");
+ clazz.setSuperclass(pool.get("java.lang.Object"));
+ CtField field = new CtField(CtClass.booleanType, "broken", clazz);
+ clazz.addField(field, "true");
+ clazz.addMethod(CtNewMethod.getter("isBroken", field));
+ clazz.addMethod(CtNewMethod.setter("setBroken", field));
+ Class _class = clazz.toClass(capability);
+
+ Object object = _class.getConstructor().newInstance();
+
+ set(_class, object, false);
+ check(_class, object, false);
+ }
+
+ /**
+ * Tests a getter only on a field with Modifier.STATIC.
+ *
+ * @throws Exception
+ */
+ public void testStaticFieldGetter() throws Exception {
+ CtClass clazz = pool.makeClass("HasStaticFieldGetter");
+ clazz.setSuperclass(pool.get("java.lang.Object"));
+ CtField field = new CtField(CtClass.booleanType, "broken", clazz);
+ field.setModifiers(Modifier.STATIC);
+ clazz.addField(field, "true");
+ clazz.addMethod(CtNewMethod.getter("isBroken", field));
+ Class _class = clazz.toClass(capability);
+
+ Object object = _class.getConstructor().newInstance();
+ check(_class, object, true);
+ }
+
+ /**
+ * Tests a getter and setter on a field with Modifier.STATIC.
+ *
+ * @throws Exception
+ */
+ public void testStaticFieldGetterSetter() throws Exception {
+ CtClass clazz = pool.makeClass("HasStaticFieldGetterSetter");
+ clazz.setSuperclass(pool.get("java.lang.Object"));
+ CtField field = new CtField(CtClass.booleanType, "broken", clazz);
+ field.setModifiers(Modifier.STATIC);
+ clazz.addField(field, "true");
+ clazz.addMethod(CtNewMethod.getter("isBroken", field));
+ clazz.addMethod(CtNewMethod.setter("setBroken", field));
+ Class _class = clazz.toClass(capability);
+
+ Object object = _class.getConstructor().newInstance();
+
+ set(_class, object, false);
+ check(_class, object, false);
+ }
+
+ private void check(Class _class, Object object, boolean shouldBe)
+ throws Exception
+ {
+ Method method = _class.getMethod("isBroken", new Class[] {});
+ Boolean result = (Boolean) method.invoke(object, new Object[] {});
+ assertEquals("boolean is wrong value",
+ shouldBe, result.booleanValue());
+ }
+
+ private void set(Class _class, Object object, boolean willBe)
+ throws Exception
+ {
+ Method method = _class.getMethod("setBroken",
+ new Class[] {Boolean.TYPE});
+ method.invoke(object, new Object[] { Boolean.valueOf(willBe)});
+ }
+}
diff --git a/src/test/javassist/bytecode/BytecodeTest.java b/src/test/javassist/bytecode/BytecodeTest.java
new file mode 100644
index 0000000..5ddf5d5
--- /dev/null
+++ b/src/test/javassist/bytecode/BytecodeTest.java
@@ -0,0 +1,835 @@
+package javassist.bytecode;
+
+import java.io.*;
+import java.lang.reflect.Method;
+import junit.framework.*;
+import javassist.*;
+import javassist.bytecode.annotation.*;
+import javassist.bytecode.SignatureAttribute.*;
+
+@SuppressWarnings("unused")
+public class BytecodeTest extends TestCase {
+ public static final String PATH = JvstTest.PATH;
+ private ClassPool loader, dloader;
+ private Loader cloader;
+
+ public BytecodeTest(String name) {
+ super(name);
+ }
+
+ protected void print(String msg) {
+ System.out.println(msg);
+ }
+
+ protected void setUp() throws Exception {
+ loader = ClassPool.getDefault();
+ dloader = new ClassPool(null);
+ dloader.appendSystemPath();
+ dloader.insertClassPath(".");
+ cloader = new Loader(dloader);
+ }
+
+ protected Object make(String name) throws Exception {
+ return cloader.loadClass(name).getConstructor().newInstance();
+ }
+
+ protected int invoke(Object target, String method) throws Exception {
+ Method m = target.getClass().getMethod(method, new Class[0]);
+ Object res = m.invoke(target, new Object[0]);
+ return ((Integer)res).intValue();
+ }
+
+ public void testByteVector() throws Exception {
+ final int N = 257;
+ Bytecode code = new Bytecode(null);
+ for (int i = 0; i < N; i++) {
+ code.add(i);
+ assertEquals(i + 1, code.length());
+ assertEquals((int)(byte)i, code.read(i));
+ code.write(i, i + 1);
+ assertEquals((int)(byte)(i + 1), code.read(i));
+ }
+
+ byte[] b = code.copy();
+ assertEquals(N, b.length);
+ for (int i = 0; i < N; i++)
+ assertEquals((int)(byte)(i + 1), b[i]);
+
+
+ code = new Bytecode(null);
+ code.add(1);
+ code.addGap(100);
+ code.add(2);
+ assertEquals(2, code.read(101));
+ }
+
+ public void testLongVector() throws Exception {
+ LongVector vec = new LongVector();
+ assertEquals(LongVector.ASIZE * LongVector.VSIZE, vec.capacity());
+ int size = LongVector.ASIZE * LongVector.VSIZE * 3;
+ for (int i = 0; i < size; i++) {
+ vec.addElement(new IntegerInfo(i, i));
+ assertEquals(i, ((IntegerInfo)vec.elementAt(i)).value);
+ assertEquals(i + 1, vec.size());
+ }
+
+ size = LongVector.ASIZE * LongVector.VSIZE * 3;
+ vec = new LongVector(size - 5);
+ assertEquals(size, vec.capacity());
+ for (int i = 0; i < size; i++) {
+ vec.addElement(new IntegerInfo(i, i));
+ assertEquals(i, ((IntegerInfo)vec.elementAt(i)).value);
+ assertEquals(i + 1, vec.size());
+ }
+ }
+
+ public void testClone() throws Exception {
+ ConstPool cp = new ConstPool("test.CloneTest");
+ Bytecode bc = new Bytecode(cp);
+ bc.add(7);
+ bc.add(11);
+ Bytecode bc2 = (Bytecode)bc.clone();
+ bc2.add(13);
+ bc2.write(0, 17);
+ assertEquals(7, bc.read(0));
+ assertEquals(2, bc.length());
+ assertEquals(3, bc2.length());
+ assertEquals(cp, bc2.getConstPool());
+ assertTrue(bc.getExceptionTable() != bc2.getExceptionTable());
+ }
+
+ public void test2byteLocalVar() throws Exception {
+ CtClass cc = loader.makeClass("test.LocalVar2");
+ CtMethod m = CtNewMethod.abstractMethod(CtClass.intType, "test",
+ null, null, cc);
+ Bytecode code = new Bytecode(cc.getClassFile().getConstPool(), 2, 300);
+ code.addIconst(1);
+ code.addIstore(255);
+ code.addIload(255);
+ code.addIstore(256);
+ code.addIload(256);
+
+ code.addLconst(1);
+ code.addLstore(255);
+ code.addLload(255);
+ code.addLstore(256);
+ code.addLload(256);
+
+ code.addFconst(1.0f);
+ code.addFstore(255);
+ code.addFload(255);
+ code.addFstore(256);
+ code.addFload(256);
+
+ code.addDconst(1.0);
+ code.addDstore(255);
+ code.addDload(255);
+ code.addDstore(256);
+ code.addDload(256);
+
+ code.addOpcode(Opcode.ACONST_NULL);
+ code.addAstore(255);
+ code.addAload(255);
+ code.addAstore(256);
+ code.addAload(256);
+
+ code.addIconst(1);
+ code.addOpcode(Opcode.IRETURN);
+
+ m.getMethodInfo().setCodeAttribute(code.toCodeAttribute());
+ m.setModifiers(Modifier.PUBLIC);
+ cc.addMethod(m);
+ cc.writeFile();
+
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "test"));
+ }
+
+ public void testBytecode() throws Exception {
+ final int N = 64;
+ Bytecode b = new Bytecode(null, 0, 0);
+ try {
+ b.write(3, 3);
+ throw new Exception("out of range");
+ }
+ catch (ArrayIndexOutOfBoundsException e) {}
+
+ try {
+ b.read(3);
+ throw new Exception("out of range");
+ }
+ catch (ArrayIndexOutOfBoundsException e) {}
+
+ for (int i = 0; i < N * 3; ++i) {
+ b.add(i % 100);
+ assertEquals(i % 100, b.read(i));
+ }
+
+ for (int i = 0; i < N * 3; ++i)
+ assertEquals(i % 100, b.read(i));
+
+ for (int i = 0; i < N * 3; ++i) {
+ b.write(i, i % 100);
+ assertEquals(i % 100, b.read(i));
+ }
+
+ for (int i = 0; i < N * 3; ++i)
+ assertEquals(i % 100, b.read(i));
+ }
+
+ public void testBytecode2() throws Exception {
+ final int N = 64;
+ Bytecode b = new Bytecode(null, 0, 0);
+
+ for (int i = 0; i < N * 3 / 16; ++i) {
+ b.addGap(16);
+ assertEquals(16 * (i + 1), b.length());
+ }
+
+ b = new Bytecode(null, 0, 0);
+
+ for (int i = 0; i < N * 3 / 10; ++i) {
+ b.addGap(10);
+ assertEquals(10 * (i + 1), b.length());
+ }
+ }
+
+ public void testDescriptor() throws Exception {
+ assertEquals("(II)", Descriptor.getParamDescriptor("(II)V"));
+ assertEquals("()", Descriptor.getParamDescriptor("()I"));
+
+ assertEquals(1, Descriptor.dataSize("I"));
+ assertEquals(2, Descriptor.dataSize("D"));
+ assertEquals(2, Descriptor.dataSize("J"));
+ assertEquals(1, Descriptor.dataSize("[J"));
+ assertEquals(1, Descriptor.dataSize("[[D"));
+ assertEquals(1, Descriptor.dataSize("LD;"));
+
+ assertEquals(-1, Descriptor.dataSize("(I)V"));
+ assertEquals(0, Descriptor.dataSize("(D)J"));
+ assertEquals(0, Descriptor.dataSize("()V"));
+ assertEquals(1, Descriptor.dataSize("()I"));
+ assertEquals(-1, Descriptor.dataSize("([DLA;)I"));
+ assertEquals(-3, Descriptor.dataSize("(BIJ)LA;"));
+ assertEquals(-3, Descriptor.dataSize("(BIJ)[D"));
+
+ boolean ok = false;
+ try {
+ Descriptor.dataSize("(Ljava/lang/String)I");
+ }
+ catch (IndexOutOfBoundsException e) {
+ print("testDescriptor(): dataSize() " + e);
+ ok = true;
+ }
+ assertTrue(ok);
+
+ ok = false;
+ try {
+ Descriptor.numOfParameters("([DLjava/lang/String)I");
+ }
+ catch (IndexOutOfBoundsException e) {
+ print("testDescriptor(): numOfParameters() " + e);
+ ok = true;
+ }
+ assertTrue(ok);
+ }
+
+ public void testDescriptor2() throws Exception {
+ assertEquals("int", Descriptor.toClassName("I"));
+ assertEquals("double[]", Descriptor.toClassName("[D"));
+ assertEquals("boolean[][]", Descriptor.toClassName("[[Z"));
+ assertEquals("java.lang.String",
+ Descriptor.toClassName("Ljava/lang/String;"));
+ assertEquals("java.lang.String[]",
+ Descriptor.toClassName("[Ljava/lang/String;"));
+ try {
+ assertEquals("Foo", Descriptor.toClassName("LFoo;;"));
+ fail("LFoo;;");
+ }
+ catch (RuntimeException e) {}
+ try {
+ assertEquals("int", Descriptor.toClassName("II"));
+ fail("II");
+ }
+ catch (RuntimeException e) {}
+ }
+
+ public void testLineNumberAttribute() throws Exception {
+ CtClass cc = loader.get("test1.LineNumber");
+ CtMethod m = cc.getDeclaredMethod("sort");
+ MethodInfo minfo = m.getMethodInfo();
+ CodeAttribute ca = minfo.getCodeAttribute();
+ LineNumberAttribute ainfo
+ = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag);
+
+ int n = ainfo.tableLength();
+ for (int i = 0; i < n; ++i)
+ print("Line " + ainfo.lineNumber(i) + " at " + ainfo.startPc(i));
+
+ print("find Line 10: " + ainfo.toStartPc(10));
+ print("find PC 30: " + ainfo.toLineNumber(30));
+
+ LineNumberAttribute.Pc pc = ainfo.toNearPc(6);
+ print("line 6: " + pc.index);
+ assertEquals(8, pc.line);
+
+ pc = ainfo.toNearPc(7);
+ print("line 7: " + pc.index);
+ assertEquals(8, pc.line);
+
+ pc = ainfo.toNearPc(8);
+ print("line 8: " + pc.index);
+ assertEquals(8, pc.line);
+
+ pc = ainfo.toNearPc(9);
+ print("line 9: " + pc.index);
+ assertEquals(9, pc.line);
+
+ pc = ainfo.toNearPc(15);
+ print("line 15: " + pc.index);
+ assertEquals(17, pc.line);
+
+ pc = ainfo.toNearPc(19);
+ print("line 19: " + pc.index);
+ assertEquals(20, pc.line);
+
+ pc = ainfo.toNearPc(21);
+ print("line 20: " + pc.index);
+ assertEquals(20, pc.line);
+
+ pc = ainfo.toNearPc(22);
+ print("line 21: " + pc.index);
+ assertEquals(20, pc.line);
+ }
+
+ public void testRenameClass() throws Exception {
+ CtClass cc = loader.get("test1.RenameClass");
+ cc.replaceClassName("test1.RenameClass2", "java.lang.String");
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(0, invoke(obj, "test"));
+ }
+
+ public void testDeprecatedAttribute() throws Exception {
+ CtClass cc = loader.get("java.lang.Thread");
+ CtMethod m = cc.getDeclaredMethod("suspend");
+ MethodInfo minfo = m.getMethodInfo();
+ DeprecatedAttribute ainfo
+ = (DeprecatedAttribute)minfo.getAttribute(DeprecatedAttribute.tag);
+ assertTrue(ainfo != null);
+ m = cc.getDeclaredMethod("toString");
+ minfo = m.getMethodInfo();
+ ainfo
+ = (DeprecatedAttribute)minfo.getAttribute(DeprecatedAttribute.tag);
+ assertTrue(ainfo == null);
+ }
+
+ public void testLocalVarAttribute() throws Exception {
+ CtClass cc = loader.get("test1.LocalVars");
+ CtMethod m = cc.getDeclaredMethod("foo");
+ MethodInfo minfo = m.getMethodInfo();
+ CodeAttribute ca = minfo.getCodeAttribute();
+ LocalVariableAttribute ainfo
+ = (LocalVariableAttribute)ca.getAttribute(
+ LocalVariableAttribute.tag);
+ assertTrue(ainfo != null);
+
+ CtClass cc2 = loader.makeClass("test1.LocalVars2");
+ CtMethod m2 = new CtMethod(m, cc2, null);
+ CodeAttribute ca2 = m2.getMethodInfo().getCodeAttribute();
+ ConstPool cp2 = ca2.getConstPool();
+ LocalVariableAttribute ainfo2
+ = (LocalVariableAttribute)ainfo.copy(cp2, null);
+ ca2.getAttributes().add(ainfo2);
+ cc2.addMethod(m2);
+ cc2.writeFile();
+ print("**** local variable table ***");
+ for (int i = 0; i < ainfo2.tableLength(); i++) {
+ String msg = ainfo2.startPc(i) + " " + ainfo2.codeLength(i)
+ + " " + ainfo2.variableName(i) + " "
+ + ainfo2.descriptor(i)
+ + " " + ainfo2.index(i);
+ print(msg);
+ if (ainfo2.variableName(i).equals("j"))
+ assertEquals("I", ainfo2.descriptor(i));
+ }
+ print("**** end ***");
+ }
+
+ public void testAnnotations() throws Exception {
+ String fname = PATH + "annotation/Test.class";
+ BufferedInputStream fin
+ = new BufferedInputStream(new FileInputStream(fname));
+ ClassFile cf = new ClassFile(new DataInputStream(fin));
+ AnnotationsAttribute attr = (AnnotationsAttribute)
+ cf.getAttribute(AnnotationsAttribute.invisibleTag);
+ String sig = attr.toString();
+ System.out.println(sig);
+
+ ClassFile cf2 = new ClassFile(false, "test1.AnnoTest",
+ "java.lang.Object");
+ cf2.addAttribute(attr.copy(cf2.getConstPool(), null));
+ AnnotationsAttribute attr2 = (AnnotationsAttribute)
+ cf2.getAttribute(AnnotationsAttribute.invisibleTag);
+
+ DataOutputStream out
+ = new DataOutputStream(new FileOutputStream("test1/AnnoTest.class"));
+ cf2.write(out);
+
+ assertEquals(sig, attr2.toString());
+ }
+
+ public void testAnnotations2() throws Exception {
+ ClassFile cf = new ClassFile(false, "test1.AnnoTest2",
+ "java.lang.Object");
+ AnnotationsAttribute anno
+ = new AnnotationsAttribute(cf.getConstPool(),
+ AnnotationsAttribute.invisibleTag);
+ ConstPool cp = cf.getConstPool();
+ Annotation a = new Annotation("Anno", cp);
+ StringMemberValue smv = new StringMemberValue("foo", cp);
+ a.addMemberValue("name", smv);
+ anno.setAnnotation(a);
+ cf.addAttribute(anno);
+
+ String fname = "test1/AnnoTest2.class";
+ DataOutputStream out
+ = new DataOutputStream(new FileOutputStream(fname));
+ cf.write(out);
+
+ BufferedInputStream fin
+ = new BufferedInputStream(new FileInputStream(fname));
+ cf = new ClassFile(new DataInputStream(fin));
+ AnnotationsAttribute attr = (AnnotationsAttribute)
+ cf.getAttribute(AnnotationsAttribute.invisibleTag);
+
+ String sig = attr.toString();
+ System.out.println(sig);
+ assertEquals("@Anno(name=\"foo\")", sig);
+ }
+
+ public void testAddClassInfo() throws Exception {
+ CtClass cc = loader.get("test1.AddClassInfo");
+ ClassFile cf = cc.getClassFile();
+ ConstPool cp = cf.getConstPool();
+ int i = cp.addClassInfo("test1.AddClassInfo");
+ assertEquals(i, cp.getThisClassInfo());
+
+ cc.addMethod(CtNewMethod.make("public int bar() { return foo(); }", cc));
+ cc.writeFile();
+ Object obj = make(cc.getName());
+ assertEquals(1, invoke(obj, "bar"));
+ }
+
+ public void testRename() throws Exception {
+ ConstPool cp = new ConstPool("test1.Foo");
+ int i = cp.addClassInfo("test1.Bar");
+ assertEquals(i, cp.addClassInfo("test1.Bar"));
+ cp.renameClass("test1/Bar", "test1/Bar2");
+ assertEquals("test1.Bar2", cp.getClassInfo(i));
+ assertEquals(i, cp.addClassInfo("test1.Bar2"));
+ int j = cp.addClassInfo("test1.Bar");
+ assertTrue(i != j);
+ assertEquals(j, cp.addClassInfo("test1.Bar"));
+ }
+
+ public void testSignature() throws Exception {
+ parseMsig("(TT;)TT;", "<> (T) T");
+ parseMsig("<S:Ljava/lang/Object;>(TS;[TS;)TT;",
+ "<S extends java.lang.Object> (S, S[]) T");
+ parseMsig("()TT;^TT;", "<> () T throws T");
+ String sig = "<T:Ljava/lang/Exception;>LPoi$Foo<Ljava/lang/String;>;LBar;LBar2;";
+ String rep = "<T extends java.lang.Exception> extends Poi.Foo<java.lang.String> implements Bar, Bar2";
+ SignatureAttribute.ClassSignature cs = SignatureAttribute.toClassSignature(sig);
+ assertEquals(rep, cs.toString());
+ CtClass c = loader.get("test3.SigAttribute");
+ CtField f = c.getDeclaredField("value");
+ SignatureAttribute a = (SignatureAttribute)f.getFieldInfo2().getAttribute(SignatureAttribute.tag);
+ assertNotNull(a);
+ f.getFieldInfo().prune(new ConstPool("test3.SigAttribute"));
+ a = (SignatureAttribute)f.getFieldInfo2().getAttribute(SignatureAttribute.tag);
+ assertNotNull(a);
+ }
+
+ private void parseMsig(String sig, String rep) throws Exception {
+ SignatureAttribute.MethodSignature ms = SignatureAttribute.toMethodSignature(sig);
+ assertEquals(rep, ms.toString());
+ }
+
+ public void testSignatureChange() throws Exception {
+ changeMsig("<S:Ljava/lang/Object;>(TS;[TS;)Ljava/lang/Object", "java/lang/Object",
+ "<S:Ljava/lang/Objec;>(TS;[TS;)Ljava/lang/Object", "java/lang/Objec");
+ changeMsig("<S:Ljava/lang/Object;>(TS;[TS;)TT;", "java/lang/Object",
+ "<S:Ljava/lang/Objec;>(TS;[TS;)TT;", "java/lang/Objec");
+ changeMsig("<S:Ljava/lang/Object;>(TS;[TS;)Ljava/lang/Object2;", "java/lang/Object",
+ "<S:Ljava/lang/Objec;>(TS;[TS;)Ljava/lang/Object2;", "java/lang/Objec");
+ changeMsig("<S:Ljava/lang/Object;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object",
+ "<S:Ljava/lang/Object2;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object2");
+ changeMsig2("<S:Ljava/lang/Object;>(TS;[TS;)TT;", "java/lang/Object",
+ "<S:Ljava/lang/Objec;>(TS;[TS;)TT;", "java/lang/Objec");
+ changeMsig2("<S:Ljava/lang/Object;>(TS;[TS;)Ljava/lang/Object2;", "java/lang/Object",
+ "<S:Ljava/lang/Objec;>(TS;[TS;)Ljava/lang/Object2;", "java/lang/Objec");
+ changeMsig2("<S:Ljava/lang/Object;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object",
+ "<S:Ljava/lang/Object2;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object2");
+ String sig = "<T:Ljava/lang/Exception;>LPoi$Foo<Ljava/lang/String;>;LBar;LBar2;";
+ //String res = "<T:Ljava/lang/Exception;>LPoi$Foo<Ljava/lang/String2;>;LBar;LBar2;";
+ changeMsig(sig, "java/lang/String", sig, "java/lang/String2");
+ changeMsig2(sig, "java/lang/String", sig, "java/lang/String2");
+ changeMsig("Ltest<TE;>.List;", "ist", "Ltest<TE;>.List;", "IST");
+ }
+
+ private void changeMsig(String old, String oldname, String result, String newname) {
+ String r = SignatureAttribute.renameClass(old, oldname, newname);
+ assertEquals(result, r);
+ }
+
+ private void changeMsig2(String old, String oldname, String result, String newname) {
+ ClassMap map = new ClassMap();
+ map.put(oldname, newname);
+ String r = SignatureAttribute.renameClass(old, map);
+ assertEquals(result, r);
+ }
+
+ public void testSignatureEncode() throws Exception {
+ BaseType bt = new BaseType("int");
+ TypeVariable tv = new TypeVariable("S");
+ ArrayType at = new ArrayType(1, tv);
+ ClassType ct1 = new ClassType("test.Foo");
+ TypeArgument ta = new TypeArgument();
+ TypeArgument ta2 = new TypeArgument(ct1);
+ TypeArgument ta3 = TypeArgument.subclassOf(ct1);
+ ClassType ct2 = new ClassType("test.Foo", new TypeArgument[] { ta, ta2, ta3 });
+ ClassType ct3 = new ClassType("test.Bar");
+ ClassType ct4 = new ClassType("test.Bar", new TypeArgument[] { ta });
+ NestedClassType ct5 = new NestedClassType(ct4, "Baz", new TypeArgument[] { ta });
+ TypeParameter tp1 = new TypeParameter("U");
+ TypeParameter tp2 = new TypeParameter("V", ct1, new ObjectType[] { ct3 });
+ ClassSignature cs = new ClassSignature(new TypeParameter[] { tp1 },
+ ct1,
+ new ClassType[] { ct2 });
+ MethodSignature ms = new MethodSignature(new TypeParameter[] { tp1, tp2 },
+ new Type[] { bt, at, ct5 }, ct3,
+ new ObjectType[] { ct1, tv });
+
+ assertEquals("<U:Ljava/lang/Object;>Ltest/Foo;Ltest/Foo<*Ltest/Foo;+Ltest/Foo;>;",
+ cs.encode());
+ assertEquals("<U:Ljava/lang/Object;V:Ltest/Foo;:Ltest/Bar;>(I[TS;Ltest/Bar<*>$Baz<*>;)Ltest/Bar;^Ltest/Foo;^TS;",
+ ms.encode());
+ }
+
+ public void testModifiers() throws Exception {
+ CtClass c = loader.get("test3.Mods");
+ c.setModifiers(Modifier.PROTECTED);
+ assertEquals(AccessFlag.PROTECTED | AccessFlag.SUPER, c.getClassFile2().getAccessFlags());
+ CtClass c2 = loader.get("test3.Mods2");
+ c2.setModifiers(Modifier.PUBLIC | c2.getModifiers());
+ assertEquals(AccessFlag.PUBLIC | AccessFlag.INTERFACE | AccessFlag.ABSTRACT,
+ c2.getClassFile2().getAccessFlags());
+
+ ClassFile cf = new ClassFile(false, "Test", null);
+ assertEquals(AccessFlag.SUPER, cf.getAccessFlags());
+ ClassFile cf2 = new ClassFile(true, "Test2", null);
+ assertEquals(AccessFlag.INTERFACE | AccessFlag.ABSTRACT, cf2.getAccessFlags());
+ }
+
+ public void testByteStream() throws Exception {
+ ByteStream bs = new ByteStream(16);
+ ByteArrayOutputStream ba = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(ba);
+ for (int i = 0; i < 100; i++) {
+ bs.write(i);
+ dos.write(i);
+ bs.writeShort(i + 1);
+ dos.writeShort(i + 1);
+ bs.writeInt(i + 2);
+ dos.writeInt(i + 2);
+ bs.writeLong(i + 3);
+ dos.writeLong(i + 3);
+ }
+
+ bs.writeLong(Long.MAX_VALUE);
+ dos.writeLong(Long.MAX_VALUE);
+ bs.writeFloat(Float.MAX_VALUE);
+ dos.writeFloat(Float.MAX_VALUE);
+ bs.writeDouble(Double.MAX_VALUE);
+ dos.writeDouble(Double.MAX_VALUE);
+ compare(bs, ba);
+ }
+
+ public void testByteStreamUtf() throws Exception {
+ ByteStream bs = new ByteStream(4);
+ ByteArrayOutputStream ba = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(ba);
+ char c2 = '\u00b4';
+ char c3 = '\u3007';
+ bs.writeUTF("abc");
+ dos.writeUTF("abc");
+ String s = "" + c2 + c2;
+ bs.writeUTF(s);
+ dos.writeUTF(s);
+
+ s = "" + c3 + c3;
+ bs.writeUTF(s);
+ dos.writeUTF(s);
+
+ s = "abcdefgh" + c2 + "123" + c3 + "456";
+ bs.writeUTF(s);
+ dos.writeUTF(s);
+
+ compare(bs, ba);
+ }
+
+ private void compare(ByteStream bs, ByteArrayOutputStream bos) {
+ byte[] bs2 = bs.toByteArray();
+ byte[] bos2 = bos.toByteArray();
+ assertEquals(bs2.length, bos2.length);
+ for (int i = 0; i < bs2.length; i++)
+ assertEquals(bs2[i], bos2[i]);
+ }
+
+ public void testConstInfos() throws Exception {
+ int n = 1;
+ Utf8Info ui1 = new Utf8Info("test", n++);
+ Utf8Info ui2 = new Utf8Info("te" + "st", n++);
+ Utf8Info ui3 = new Utf8Info("test2", n++);
+ assertTrue(ui1.hashCode() == ui2.hashCode());
+ assertTrue(ui1.equals(ui1));
+ assertTrue(ui1.equals(ui2));
+ assertFalse(ui1.equals(ui3));
+ assertFalse(ui1.equals(null));
+
+ ClassInfo ci1 = new ClassInfo(ui1.index, n++);
+ ClassInfo ci2 = new ClassInfo(ui1.index, n++);
+ ClassInfo ci3 = new ClassInfo(ui2.index, n++);
+ ClassInfo ci4 = new ClassInfo(ui3.index, n++);
+ assertTrue(ci1.hashCode() == ci2.hashCode());
+ assertTrue(ci1.equals(ci1));
+ assertTrue(ci1.equals(ci2));
+ assertFalse(ci1.equals(ci3));
+ assertFalse(ci1.equals(ci4));
+ assertFalse(ci1.equals(ui1));
+ assertFalse(ci1.equals(null));
+
+ NameAndTypeInfo ni1 = new NameAndTypeInfo(ui1.index, ui3.index, n++);
+ NameAndTypeInfo ni2 = new NameAndTypeInfo(ui1.index, ui3.index, n++);
+ NameAndTypeInfo ni3 = new NameAndTypeInfo(ui1.index, ui1.index, n++);
+ NameAndTypeInfo ni4 = new NameAndTypeInfo(ui3.index, ui3.index, n++);
+ assertTrue(ni1.hashCode() == ni2.hashCode());
+ assertTrue(ni1.equals(ni1));
+ assertTrue(ni1.equals(ni2));
+ assertFalse(ni1.equals(ni3));
+ assertFalse(ni1.equals(ni4));
+ assertFalse(ni1.equals(ci1));
+ assertFalse(ni1.equals(null));
+
+ MethodrefInfo mi1 = new MethodrefInfo(ui1.index, ui3.index, n++);
+ MethodrefInfo mi2 = new MethodrefInfo(ui1.index, ui3.index, n++);
+ MethodrefInfo mi3 = new MethodrefInfo(ui1.index, ui1.index, n++);
+ MethodrefInfo mi4 = new MethodrefInfo(ui2.index, ui3.index, n++);
+ assertTrue(mi1.hashCode() == mi2.hashCode());
+ assertTrue(mi1.equals(mi1));
+ assertTrue(mi1.equals(mi2));
+ assertFalse(mi1.equals(mi3));
+ assertFalse(mi1.equals(mi4));
+ assertFalse(mi1.equals(ci1));
+ assertFalse(mi1.equals(null));
+
+ FieldrefInfo field1 = new FieldrefInfo(ui1.index, ui3.index, n++);
+ FieldrefInfo field2 = new FieldrefInfo(ui1.index, ui1.index, n++);
+ FieldrefInfo field3 = new FieldrefInfo(ui1.index, ui1.index, n++);
+ InterfaceMethodrefInfo intf1 = new InterfaceMethodrefInfo(ui1.index, ui3.index, n++);
+ InterfaceMethodrefInfo intf2 = new InterfaceMethodrefInfo(ui1.index, ui3.index, n++);
+ assertFalse(mi1.equals(field1));
+ assertFalse(field1.equals(mi1));
+ assertTrue(field2.equals(field3));
+ assertFalse(mi1.equals(field2));
+ assertFalse(mi1.equals(intf1));
+ assertFalse(intf1.equals(mi1));
+ assertTrue(intf1.equals(intf2));
+
+ StringInfo si1 = new StringInfo(ui1.index, n++);
+ StringInfo si2 = new StringInfo(ui1.index, n++);
+ StringInfo si3 = new StringInfo(ui2.index, n++);
+ assertTrue(si1.hashCode() == si2.hashCode());
+ assertTrue(si1.equals(si1));
+ assertTrue(si1.equals(si2));
+ assertFalse(si1.equals(si3));
+ assertFalse(si1.equals(ci1));
+ assertFalse(si1.equals(null));
+
+ IntegerInfo ii1 = new IntegerInfo(12345, n++);
+ IntegerInfo ii2 = new IntegerInfo(12345, n++);
+ IntegerInfo ii3 = new IntegerInfo(-12345, n++);
+ assertTrue(ii1.hashCode() == ii2.hashCode());
+ assertTrue(ii1.equals(ii1));
+ assertTrue(ii1.equals(ii2));
+ assertFalse(ii1.equals(ii3));
+ assertFalse(ii1.equals(ci1));
+ assertFalse(ii1.equals(null));
+
+ FloatInfo fi1 = new FloatInfo(12345.0F, n++);
+ FloatInfo fi2 = new FloatInfo(12345.0F, n++);
+ FloatInfo fi3 = new FloatInfo(-12345.0F, n++);
+ assertTrue(fi1.hashCode() == fi2.hashCode());
+ assertTrue(fi1.equals(fi1));
+ assertTrue(fi1.equals(fi2));
+ assertFalse(fi1.equals(fi3));
+ assertFalse(fi1.equals(ci1));
+ assertFalse(fi1.equals(null));
+
+ LongInfo li1 = new LongInfo(12345L, n++);
+ LongInfo li2 = new LongInfo(12345L, n++);
+ LongInfo li3 = new LongInfo(-12345L, n++);
+ assertTrue(li1.hashCode() == li2.hashCode());
+ assertTrue(li1.equals(li1));
+ assertTrue(li1.equals(li2));
+ assertFalse(li1.equals(li3));
+ assertFalse(li1.equals(ci1));
+ assertFalse(li1.equals(null));
+
+ DoubleInfo di1 = new DoubleInfo(12345.0, n++);
+ DoubleInfo di2 = new DoubleInfo(12345.0, n++);
+ DoubleInfo di3 = new DoubleInfo(-12345.0, n++);
+ assertTrue(di1.hashCode() == di2.hashCode());
+ assertTrue(di1.equals(di1));
+ assertTrue(di1.equals(di2));
+ assertFalse(di1.equals(di3));
+ assertFalse(di1.equals(ci1));
+ assertFalse(di1.equals(null));
+ }
+
+ public void testConstInfoAdd() {
+ ConstPool cp = new ConstPool("test.Tester");
+ assertEquals("test.Tester", cp.getClassName());
+ int n0 = cp.addClassInfo("test.Foo");
+ assertEquals(n0, cp.addClassInfo("test.Foo"));
+ int n1 = cp.addUtf8Info("test.Bar");
+ assertEquals(n1, cp.addUtf8Info("test.Bar"));
+ int n2 = cp.addUtf8Info("()V");
+ assertEquals(n2, cp.addUtf8Info("()V"));
+ assertTrue(n1 != n2);
+ int n3 = cp.addNameAndTypeInfo(n1, n2);
+ assertEquals(n3, cp.addNameAndTypeInfo(n1, n2));
+ assertEquals(n3, cp.addNameAndTypeInfo("test.Bar", "()V"));
+ int n4 = cp.addNameAndTypeInfo("test.Baz", "()V");
+ assertTrue(n3 != n4);
+ assertTrue(n3 != cp.addNameAndTypeInfo(cp.addUtf8Info("test.Baz"), n2));
+ int n5 = cp.addFieldrefInfo(n0, n3);
+ assertEquals(n5, cp.addFieldrefInfo(n0, n3));
+ assertTrue(n5 != cp.addFieldrefInfo(n0, n4));
+ assertTrue(cp.addMethodrefInfo(n0, n3) == cp.addMethodrefInfo(n0, n3));
+ assertTrue(cp.addMethodrefInfo(n0, "test", "()B") == cp.addMethodrefInfo(n0, "test", "()B"));
+ assertTrue(cp.addMethodrefInfo(n0, "test", "()B") != cp.addMethodrefInfo(n0, "test", "()I"));
+ assertTrue(n5 != cp.addInterfaceMethodrefInfo(n0, n3));
+ assertTrue(cp.addInterfaceMethodrefInfo(n0, "test", "()B")
+ == cp.addInterfaceMethodrefInfo(n0, "test", "()B"));
+ assertTrue(cp.addInterfaceMethodrefInfo(n0, "test", "()B")
+ != cp.addInterfaceMethodrefInfo(n0, "test", "()I"));
+ int n6 = cp.addStringInfo("foobar");
+ assertEquals(n6, cp.addStringInfo("foobar"));
+ assertTrue(n6 != cp.addStringInfo("foobar2"));
+ int n7 = cp.addIntegerInfo(123);
+ assertEquals(n7, cp.addIntegerInfo(123));
+ assertTrue(n7 != cp.addIntegerInfo(-123));
+ int n8 = cp.addFloatInfo(123);
+ assertEquals(n8, cp.addFloatInfo(123.0F));
+ assertTrue(n8 != cp.addFloatInfo(-123.0F));
+ int n9 = cp.addLongInfo(1234L);
+ assertEquals(n9, cp.addLongInfo(1234L));
+ assertTrue(n9 != cp.addLongInfo(-1234L));
+ int n10 = cp.addDoubleInfo(1234.0);
+ assertEquals(n10, cp.addDoubleInfo(1234.0));
+ assertTrue(n10 != cp.addDoubleInfo(-1234.0));
+
+ cp.prune();
+ assertEquals(n1, cp.addUtf8Info("test.Bar"));
+ assertEquals(n0, cp.addClassInfo("test.Foo"));
+ assertEquals(n10, cp.addDoubleInfo(1234.0));
+ }
+
+ public void testRenameInConstPool() {
+ ConstPool cp = new ConstPool("test.Tester");
+ int n1 = cp.addClassInfo("test.Foo");
+ int n2 = cp.addClassInfo("test.Bar");
+ int n3 = cp.addClassInfo("test.Baz");
+ int n4 = cp.addNameAndTypeInfo("foo", "(Ltest/Foo;)V");
+ int n5 = cp.addNameAndTypeInfo("bar", "(Ltest/Bar;)V");
+ int n6 = cp.addNameAndTypeInfo("baz", "(Ltest/Baz;)V");
+ int n7 = cp.addClassInfo("[Ltest/Foo;");
+ int n8 = cp.addClassInfo("[Ltest/Bar;");
+
+ cp.renameClass("test/Foo", "test/Foo2");
+ assertEquals("test.Foo2", cp.getClassInfo(n1));
+ assertEquals("(Ltest/Foo2;)V", cp.getUtf8Info(cp.getNameAndTypeDescriptor(n4)));
+ assertTrue(cp.addClassInfo("test.Foo2") == n1);
+ assertTrue(cp.addClassInfo("test.Foo") != n1);
+ assertTrue(cp.addNameAndTypeInfo("foo", "(Ltest/Foo2;)V") == n4);
+ assertTrue(cp.addNameAndTypeInfo("foo", "(Ltest/Foo;)V") != n4);
+ assertEquals("[Ltest.Foo2;", cp.getClassInfo(n7));
+
+ ClassMap map = new ClassMap();
+ map.put("test.Bar", "test.Bar2");
+ map.put("test.Baz", "test.Baz2");
+ cp.renameClass(map);
+ assertEquals("test.Bar2", cp.getClassInfo(n2));
+ assertEquals("(Ltest/Bar2;)V", cp.getUtf8Info(cp.getNameAndTypeDescriptor(n5)));
+ assertTrue(cp.addClassInfo("test.Bar2") == n2);
+ assertTrue(cp.addClassInfo("test.Bar") != n2);
+ assertTrue(cp.addNameAndTypeInfo("bar", "(Ltest/Bar2;)V") == n5);
+ assertTrue(cp.addNameAndTypeInfo("bar", "(Ltest/Bar;)V") != n5);
+ assertEquals("[Ltest.Bar2;", cp.getClassInfo(n8));
+ }
+
+ public void testInvokeDynamic() throws Exception {
+ CtClass cc = loader.get("test4.InvokeDyn");
+ ClassFile cf = cc.getClassFile();
+ ConstPool cp = cf.getConstPool();
+
+ Bytecode code = new Bytecode(cp, 0, 1);
+ code.addAload(0);
+ code.addIconst(9);
+ code.addLdc("nine");
+ code.addInvokedynamic(0, "call", "(ILjava/lang/String;)I");
+ code.addOpcode(Opcode.SWAP);
+ code.addOpcode(Opcode.POP);
+ code.addOpcode(Opcode.IRETURN);
+
+ MethodInfo minfo = new MethodInfo(cp, "test", "()I");
+ minfo.setCodeAttribute(code.toCodeAttribute());
+ minfo.setAccessFlags(AccessFlag.PUBLIC);
+ minfo.rebuildStackMapIf6(loader, cf);
+ cf.addMethod(minfo);
+
+ cf.addMethod(new MethodInfo(cp, "test2", minfo, null));
+ int mtIndex = cp.addMethodTypeInfo(cp.addUtf8Info("(I)V"));
+
+ String desc
+ = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)" +
+ "Ljava/lang/invoke/CallSite;";
+ int mri = cp.addMethodrefInfo(cp.addClassInfo(cc.getName()), "boot", desc);
+ int mhi = cp.addMethodHandleInfo(ConstPool.REF_invokeStatic, mri);
+ int[] args = new int[0];
+ BootstrapMethodsAttribute.BootstrapMethod[] bms
+ = new BootstrapMethodsAttribute.BootstrapMethod[1];
+ bms[0] = new BootstrapMethodsAttribute.BootstrapMethod(mhi, args);
+
+ cf.addAttribute(new BootstrapMethodsAttribute(cp, bms));
+ cc.writeFile();
+
+ Object obj = make(cc.getName());
+ assertEquals(9, invoke(obj, "test"));
+
+ ClassPool cp2 = new ClassPool();
+ cp2.appendClassPath(".");
+ CtClass cc2 = cp2.get(cc.getName());
+ assertEquals("test4.InvokeDyn", cc2.getClassFile().getName());
+ ConstPool cPool2 = cc2.getClassFile().getConstPool();
+ assertEquals("(I)V", cPool2.getUtf8Info(cPool2.getMethodTypeInfo(mtIndex)));
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Bytecode Tests");
+ suite.addTestSuite(BytecodeTest.class);
+ return suite;
+ }
+}
diff --git a/src/test/javassist/bytecode/CodeAnalyzerTest.java b/src/test/javassist/bytecode/CodeAnalyzerTest.java
new file mode 100644
index 0000000..b6b3318
--- /dev/null
+++ b/src/test/javassist/bytecode/CodeAnalyzerTest.java
@@ -0,0 +1,48 @@
+package javassist.bytecode;
+
+import java.util.zip.*;
+import java.util.Enumeration;
+import java.util.List;
+import java.io.*;
+
+@SuppressWarnings({"rawtypes","resource"})
+public class CodeAnalyzerTest {
+ public static void main(String[] args) throws Exception {
+ ZipFile zfile = new ZipFile(args[0]);
+ Enumeration e = zfile.entries();
+ while (e.hasMoreElements()) {
+ ZipEntry zip = (ZipEntry)e.nextElement();
+ if (zip.getName().endsWith(".class"))
+ test(zfile.getInputStream(zip));
+ }
+ }
+
+ static void test(InputStream is) throws Exception {
+ is = new BufferedInputStream(is);
+ ClassFile cf = new ClassFile(new DataInputStream(is));
+ is.close();
+ List list = cf.getMethods();
+ int n = list.size();
+ for (int i = 0; i < n; ++i) {
+ MethodInfo minfo = (MethodInfo)list.get(i);
+ CodeAttribute ca = minfo.getCodeAttribute();
+ if (ca != null) {
+ try {
+ int max = ca.getMaxStack();
+ int newMax = ca.computeMaxStack();
+ if (max != newMax)
+ System.out.println(max + " -> " + newMax +
+ " for " + minfo.getName() + " (" +
+ minfo.getDescriptor() + ") in " +
+ cf.getName());
+ }
+ catch (BadBytecode e) {
+ System.out.println(e.getMessage() +
+ " for " + minfo.getName() + " (" +
+ minfo.getDescriptor() + ") in " +
+ cf.getName());
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/javassist/bytecode/Gap0Example.class b/src/test/javassist/bytecode/Gap0Example.class
new file mode 100644
index 0000000..f705a90
--- /dev/null
+++ b/src/test/javassist/bytecode/Gap0Example.class
Binary files differ
diff --git a/src/test/javassist/bytecode/InsertGap0.java b/src/test/javassist/bytecode/InsertGap0.java
new file mode 100644
index 0000000..425f12d
--- /dev/null
+++ b/src/test/javassist/bytecode/InsertGap0.java
@@ -0,0 +1,231 @@
+package javassist.bytecode;
+
+import javassist.*;
+
+@SuppressWarnings("unused")
+final class Gap0Example {
+ public static int counter = 1;
+
+ public Gap0Example() {}
+
+ public int run(int x) { return counter; }
+
+ private static final int INTVALUE = 100000;
+ private int i1, i2, i3, i4, i5, i6, i7, i8, i9, i10;
+ private int i11, i12, i13, i14, i15, i16, i17, i18, i19, i20;
+ private int i21, i22, i23, i24, i25, i26, i27, i28, i29, i30;
+ private int i31, i32, i33, i34, i35, i36, i37, i38, i39, i40;
+
+ public void doit() {
+
+ i1 = INTVALUE;
+ i2 = INTVALUE;
+ i3 = INTVALUE;
+ i4 = INTVALUE;
+ i5 = INTVALUE;
+ i6 = INTVALUE;
+ i7 = INTVALUE;
+ i8 = INTVALUE;
+ i9 = INTVALUE;
+ i10 = INTVALUE;
+ i11 = INTVALUE;
+ i12 = INTVALUE;
+ i13 = INTVALUE;
+ i14 = INTVALUE;
+ i15 = INTVALUE;
+ i16 = INTVALUE;
+ i17 = INTVALUE;
+ i18 = INTVALUE;
+ i19 = INTVALUE;
+ i20 = INTVALUE;
+ i21 = INTVALUE;
+ i22 = INTVALUE;
+ i23 = INTVALUE;
+ i24 = INTVALUE;
+ i25 = INTVALUE;
+ i26 = INTVALUE;
+ i27 = INTVALUE;
+ i28 = INTVALUE;
+ i29 = INTVALUE;
+ i20 = INTVALUE;
+ i21 = INTVALUE;
+ i22 = INTVALUE;
+ i23 = INTVALUE;
+ i24 = INTVALUE;
+ i25 = INTVALUE;
+ i26 = INTVALUE;
+ i27 = INTVALUE;
+ i28 = INTVALUE;
+ i29 = INTVALUE;
+ i30 = INTVALUE;
+ i31 = INTVALUE;
+ i32 = INTVALUE;
+ i33 = INTVALUE;
+ i34 = INTVALUE;
+ i35 = INTVALUE;
+ i36 = INTVALUE;
+ i37 = INTVALUE;
+ i38 = INTVALUE;
+ i39 = INTVALUE;
+ i40 = INTVALUE;
+ }
+}
+
+@SuppressWarnings("unused")
+final class Gap0Example2 {
+ public static int counter = 1;
+
+ public Gap0Example2() {}
+
+ public int run(int x) { return counter; }
+
+ private static final int INTVALUE = 100000;
+ private int i1, i2, i3, i4, i5, i6, i7, i8, i9, i10;
+ private int i11, i12, i13, i14, i15, i16, i17, i18, i19, i20;
+ private int i21, i22, i23, i24, i25, i26, i27, i28, i29, i30;
+ private int i31, i32, i33, i34, i35, i36, i37, i38, i39, i40;
+
+ public int run2(int x) {
+ switch (x) {
+ case 0:
+ i1 = INTVALUE;
+ i2 = INTVALUE;
+ i3 = INTVALUE;
+ i4 = INTVALUE;
+ i5 = INTVALUE;
+ i6 = INTVALUE;
+ i7 = INTVALUE;
+ break;
+ case 100:
+ i8 = INTVALUE;
+ i9 = INTVALUE;
+ i10 = INTVALUE;
+ i11 = INTVALUE;
+ i12 = INTVALUE;
+ i13 = INTVALUE;
+ i14 = INTVALUE;
+ break;
+ default:
+ i15 = INTVALUE;
+ i16 = INTVALUE;
+ i17 = INTVALUE;
+ if (x > 0) {
+ i18 = INTVALUE;
+ i19 = INTVALUE;
+ i20 = INTVALUE;
+ i21 = INTVALUE;
+ i22 = INTVALUE;
+ i23 = INTVALUE;
+ i24 = INTVALUE;
+ }
+ i25 = INTVALUE;
+ i26 = INTVALUE;
+ i27 = INTVALUE;
+ i28 = INTVALUE;
+ i29 = INTVALUE;
+ i20 = INTVALUE;
+ i21 = INTVALUE;
+ i22 = INTVALUE;
+ i23 = INTVALUE;
+ i24 = INTVALUE;
+ i25 = INTVALUE;
+ i26 = INTVALUE;
+ i27 = INTVALUE;
+ i28 = INTVALUE;
+ i29 = INTVALUE;
+ i30 = INTVALUE;
+ i31 = INTVALUE;
+ i32 = INTVALUE;
+ i33 = INTVALUE;
+ i34 = INTVALUE;
+ i35 = INTVALUE;
+ i36 = INTVALUE;
+ i37 = INTVALUE;
+ i38 = INTVALUE;
+ i39 = INTVALUE;
+ i40 = INTVALUE;
+ break;
+ }
+ switch (x) {
+ case 0:
+ break;
+ default:
+ return x + 1;
+ }
+
+ return x;
+ }
+}
+
+@SuppressWarnings({"rawtypes","unchecked","unused"})
+public final class InsertGap0 extends JvstTestRoot {
+ public InsertGap0(String name) {
+ super(name);
+ }
+
+ public void testExample() throws Throwable {
+ ClassPool pool = ClassPool.getDefault();
+ CtClass cc = pool.get("javassist.bytecode.Gap0Example");
+ CtMethod[] ms = cc.getDeclaredMethods();
+ for (int i = 0; i < ms.length; i++) {
+ addMethod(ms[i], cc);
+ }
+
+ cc.setModifiers(Modifier.PUBLIC | Modifier.FINAL);
+ cc.addField(new CtField(CtClass.intType, "i", cc), "++counter");
+ boolean p = cc.stopPruning(true);
+ cc.writeFile();
+ Class c = cc.toClass(ClassFile.class);
+ cc.stopPruning(p);
+
+ Object obj = c.getConstructor().newInstance();
+ assertEquals(2, invoke(obj, "run", 0));
+ }
+
+ public void testExample2() throws Throwable {
+ ClassPool pool = ClassPool.getDefault();
+ CtClass cc = pool.get("javassist.bytecode.Gap0Example2");
+ CtMethod[] ms = cc.getDeclaredMethods();
+ for (int i = 0; i < ms.length; i++) {
+ addMethod(ms[i], cc);
+ }
+
+ cc.setModifiers(Modifier.PUBLIC | Modifier.FINAL);
+ cc.addField(new CtField(CtClass.intType, "i", cc), "++counter");
+ boolean p = cc.stopPruning(true);
+ cc.writeFile();
+ Class c = cc.toClass(ClassFile.class);
+ cc.stopPruning(p);
+
+ Object obj = c.getConstructor().newInstance();
+ assertEquals(0, invoke(obj, "run2", 0));
+ }
+
+ private void addMethod(CtMethod method, CtClass target)
+ throws CannotCompileException, NotFoundException {
+
+ CtClass[] ts = method.getParameterTypes();
+ CtClass[] newts = new CtClass[ts.length + 1];
+ for (int i = 0; i < ts.length; i++) {
+ newts[i] = ts[i];
+ }
+ ClassPool p = method.getDeclaringClass().getClassPool();
+ newts[ts.length] = target;
+
+ CtMethod m =
+ CtNewMethod.make(
+ method.getModifiers(),
+ method.getReturnType(),
+ method.getName(),
+ newts,
+ method.getExceptionTypes(),
+ null,
+ method.getDeclaringClass());
+
+ m.setBody(method, null);
+
+ CodeAttribute ca = m.getMethodInfo().getCodeAttribute();
+ ca.setMaxLocals(ca.getMaxLocals() + 1);
+ target.addMethod(m);
+ }
+}
diff --git a/src/test/javassist/bytecode/StackMapTest.java b/src/test/javassist/bytecode/StackMapTest.java
new file mode 100644
index 0000000..153f75a
--- /dev/null
+++ b/src/test/javassist/bytecode/StackMapTest.java
@@ -0,0 +1,833 @@
+package javassist.bytecode;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+
+import javassist.ClassPool;
+import javassist.CodeConverter;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import javassist.JvstTest;
+import javassist.Loader;
+import javassist.bytecode.stackmap.MapMaker;
+import javassist.bytecode.stackmap.TypeData;
+import junit.framework.TestCase;
+
+@SuppressWarnings({"rawtypes","unused"})
+public class StackMapTest extends TestCase {
+ public static final String PATH = JvstTest.PATH;
+ private ClassPool loader, dloader;
+ private Loader cloader;
+
+ public StackMapTest(String name) { super(name); }
+
+ protected void setUp() throws Exception {
+ loader = ClassPool.getDefault();
+ dloader = new ClassPool(null);
+ dloader.appendSystemPath();
+ dloader.insertClassPath(".");
+ cloader = new Loader(dloader);
+ }
+
+ protected Object make(String name) throws Exception {
+ return cloader.loadClass(name).getConstructor().newInstance();
+ }
+
+ protected int invoke(Object target, String method) throws Exception {
+ Method m = target.getClass().getMethod(method, new Class[0]);
+ Object res = m.invoke(target, new Object[0]);
+ return ((Integer)res).intValue();
+ }
+
+ protected static void rebuildStackMaps(CtClass cc) throws BadBytecode, IOException {
+ ClassPool cp = cc.getClassPool();
+ Iterator it = cc.getClassFile().getMethods().iterator();
+ while (it.hasNext()) {
+ MethodInfo minfo = (MethodInfo)it.next();
+ rebuildStackMap(cc, cp, minfo);
+ }
+ }
+
+ protected static void rebuildStackMap(CtClass cc, ClassPool cp, MethodInfo minfo) throws BadBytecode, IOException {
+ CodeAttribute ca = minfo.getCodeAttribute();
+ if (ca != null) {
+ StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag);
+ if (smt != null) {
+ String data = readSmt(smt);
+ StackMapTable smt2 = MapMaker.make(cp, minfo);
+ String data2 = readSmt(smt2);
+ try {
+ assertEquals(cc.getName() + ":" + minfo.getName() + ":" + minfo.getDescriptor(),
+ data, data2);
+ }
+ catch (junit.framework.ComparisonFailure e) {
+ System.out.println("*** " + cc.getName() + ":" + minfo.getName() + ":" + minfo.getDescriptor());
+ smt.println(System.out);
+ System.out.println("---");
+ smt2.println(System.out);
+ }
+ }
+ }
+ }
+
+ protected static void rebuildStackMaps2(CtClass cc) throws BadBytecode {
+ ClassPool cp = cc.getClassPool();
+ Iterator it = cc.getClassFile().getMethods().iterator();
+ while (it.hasNext()) {
+ MethodInfo minfo = (MethodInfo)it.next();
+ minfo.rebuildStackMap(cp);
+ }
+ }
+
+ protected static String readSmt(StackMapTable smt) throws BadBytecode, IOException {
+ if (smt == null)
+ return "";
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PrintStream pout = new PrintStream(out);
+ smt.println(pout);
+ pout.close();
+ return out.toString();
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (args.length == 2 && args[0].equals("-c")) {
+ CtClass cc = ClassPool.getDefault().get(args[0].replace('/', '.'));
+ rebuildStackMaps(cc);
+ }
+ else {
+ for (int i = 0; i < args.length; i++) {
+ CtClass cc = ClassPool.getDefault().get(getClassName(args[i]));
+ System.out.println(cc.getName());
+ rebuildStackMaps2(cc);
+ cc.writeFile("rebuild");
+ }
+ }
+ }
+
+ public static String getClassName(String fileName) {
+ Matcher m = Pattern.compile("(.*)\\.class").matcher(fileName);
+ if (m.matches())
+ return m.group(1).replace('/', '.');
+ else
+ return fileName;
+ }
+
+ public void testCommonSuper() throws Exception {
+ CtClass type = loader.get("javassist.CtClassType[]");
+ CtClass base = loader.get("javassist.CtClass[]");
+ CtClass array = loader.get("javassist.CtArray[]");
+ CtClass array2 = loader.get("javassist.CtArray[][]");
+ CtClass str = loader.get("java.lang.String[]");
+ CtClass strObj = loader.get("java.lang.String");
+ CtClass obj = loader.get("java.lang.Object[]");
+ CtClass objObj = loader.get("java.lang.Object");
+
+ assertEquals(base, TypeData.commonSuperClassEx(type, base));
+ assertEquals(base, TypeData.commonSuperClassEx(base, type));
+ assertEquals(base, TypeData.commonSuperClassEx(array, type));
+ assertEquals(obj, TypeData.commonSuperClassEx(base, str));
+ assertEquals(objObj, TypeData.commonSuperClassEx(strObj, str));
+ assertEquals(obj, TypeData.commonSuperClassEx(array, array2));
+ assertEquals(obj, TypeData.commonSuperClassEx(base, array2));
+ }
+
+ public void testRebuild() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T1");
+ rebuildStackMaps2(cc);
+ //Class c = cc.toClass();
+ //Object t1 = c.getConstructor().newInstance();
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(3, invoke(t1, "test"));
+ }
+
+ public static interface Intf {
+ int foo();
+ }
+
+ public static class C1 implements Intf {
+ public int foo() { return 0; }
+ }
+
+ public static class C2 implements Intf {
+ public int foo() { return 3; }
+ }
+
+ public static class T1 {
+ public int test() {
+ return foo(-1);
+ }
+
+ public int foo(int i) {
+ Intf obj;
+ if (i > 0)
+ obj = new C1();
+ else
+ obj = new C2();
+
+ return obj.foo();
+ }
+ }
+
+ public void testRebuild2() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$C3");
+ rebuildStackMaps2(cc);
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(7, invoke(t1, "test"));
+ }
+
+ public static class C3 {
+ int value;
+ public C3(int i) {
+ value = i;
+ }
+ public C3(boolean b) {
+ this(b ? 7 : 10);
+ }
+ public C3() { this(true); }
+ public int test() { return value; }
+ }
+
+ public void testRebuild3() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T3");
+ rebuildStackMaps2(cc);
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(1100, invoke(t1, "test"));
+ }
+
+ public static interface Intf2 {
+ int bar();
+ }
+
+ public static class D1 extends C1 implements Intf2 {
+ public int bar() { return 10; }
+ }
+
+ public static class D2 extends C1 implements Intf2 {
+ public int bar() { return 100; }
+ }
+
+ public static class T3 {
+ public int bar(Intf2 i) { return 1000; }
+
+ public int test() {
+ return foo(-1);
+ }
+
+ public int foo(int i) {
+ Intf2 obj;
+ if (i > 0)
+ obj = new D1();
+ else
+ obj = new D2();
+
+ System.out.println(obj.toString());
+ return obj.bar() + bar(obj);
+ }
+ }
+
+ public void testRebuildArray() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T4");
+ rebuildStackMaps2(cc);
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(30, invoke(t1, "test"));
+ }
+
+ public static class T4 {
+ public int test() {
+ return foo(3) + foo2(3) + foo3(3) + foo4(3);
+ }
+
+ public int foo(int i) {
+ C1[] a;
+ if (i > 0)
+ a = new D1[1];
+ else
+ a = new D2[1];
+
+ if (i > 0)
+ a[0] = new D1();
+ else
+ a[0] = new D2();
+
+ return a[0].foo();
+ }
+
+ public int foo2(int i) {
+ Intf2[] a;
+ if (i > 0)
+ a = new D1[1];
+ else
+ a = new D2[1];
+
+ if (i > 0)
+ a[0] = new D1();
+ else
+ a[0] = new D2();
+
+ return a[0].bar();
+ }
+
+ public int foo3(int i) {
+ Intf2[] a = null;
+ if (i > 0)
+ a = new D1[1];
+
+ a[0] = new D1();
+ return a[0].bar();
+ }
+
+ public int foo4(int i) {
+ Intf2[] a = new Intf2[1];
+ if (i > 0)
+ a[0] = new D1();
+ return a[0].bar();
+ }
+ }
+
+ public void testRebuildConstructor() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T5");
+ rebuildStackMaps2(cc);
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(123, invoke(t1, "test"));
+ }
+
+ public static class T5 {
+ int count;
+ public T5() { count = 0; }
+ public T5(int k) {
+ if (k > 0) count = 10;
+ else count = 100;
+ count++;
+ }
+ public T5(double d) {
+ this(d > 0.0 ? 1 : -1);
+ if (d > 1.0) count += 10;
+ else count += 100;
+ count++;
+ }
+ public int test() {
+ return new T5(3).count + new T5(1.0).count;
+ }
+ }
+
+ public void testRebuildConstructor2() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T6");
+ rebuildStackMaps2(cc);
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(101, invoke(t1, "test2"));
+ }
+
+ public static class T6 {
+ public int test2() {
+ T5 t0 = new T5();
+ T5 t = new T5(t0.count > 0 ? (new T5(t0.count > 0 ? 1 : -1).count) : -1);
+ if (t0.count > 0)
+ t.count += 1000;
+ return t.count;
+ }
+ }
+
+ public void testSwitchCase() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T7");
+ // CodeConverter conv = new CodeConverter();
+ // conv.replaceNew(cc, cc, "make2");
+ // cc.instrument(conv);
+ StringBuffer sbuf = new StringBuffer("String s;");
+ for (int i = 0; i < 130; i++)
+ sbuf.append("s =\"" + i + "\";");
+
+ cc.getDeclaredMethod("foo").insertBefore(sbuf.toString());
+ cc.getDeclaredMethod("test2").setBody(loader.get("javassist.bytecode.StackMapTest$T8").getDeclaredMethod("test2"), null);
+ //rebuildStackMaps2(cc);
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(110, invoke(t1, "test"));
+ }
+
+ public static class T7 {
+ int value = 1;
+ T7 t7;
+ public static T7 make2() { return null; }
+ public int foo() { return 1; }
+ public int test() { return test2(10); }
+ public int test2(int k) { return k; }
+ }
+
+ public static class T8 {
+ public int test2(int k) {
+ String s = "abc";
+ T7 t = k > 0 ? new T7() : new T7();
+ switch (k) {
+ case 0:
+ t = new T7();
+ k += t.value;
+ break;
+ case 10:
+ k += 100;
+ break;
+ }
+ return k;
+ }
+ }
+
+ public void testSwitchCase2() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T7b");
+ StringBuffer sbuf = new StringBuffer("String s;");
+ for (int i = 0; i < 130; i++)
+ sbuf.append("s =\"" + i + "\";");
+
+ cc.getDeclaredMethod("foo").insertBefore(sbuf.toString());
+ cc.getDeclaredMethod("test2").setBody(loader.get("javassist.bytecode.StackMapTest$T8b").getDeclaredMethod("test2"), null);
+ rebuildStackMaps2(cc);
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(110, invoke(t1, "test"));
+ }
+
+ public static class T7b {
+ int value = 1;
+ T7b t7;
+ public static T7b make2() { return null; }
+ public int foo() { return 1; }
+ public int test() { return test2(10); }
+ public int test2(int k) { return k; }
+ }
+
+ public static class T8b {
+ public int test2(int k) {
+ String s = "abc";
+ T7b t = k > 0 ? new T7b() : new T7b();
+ switch (k) {
+ case 0:
+ t = new T7b();
+ k += t.value;
+ break;
+ case 10:
+ k += 100;
+ break;
+ }
+ return k;
+ }
+ }
+
+ public void testSwitchCase3() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T7c");
+ StringBuffer sbuf = new StringBuffer("String s;");
+ for (int i = 0; i < 130; i++)
+ sbuf.append("s =\"" + i + "\";");
+
+ cc.getDeclaredMethod("foo").insertBefore(sbuf.toString());
+ cc.getDeclaredMethod("test2").setBody(loader.get("javassist.bytecode.StackMapTest$T8c").getDeclaredMethod("test2"), null);
+
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(100, invoke(t1, "test"));
+ }
+
+ public static class T7c {
+ int value = 1;
+ T7b t7;
+ public static T7b make2() { return null; }
+ public static void print(String s) {}
+ public int foo() { return 1; }
+ public int test() { return test2(10); }
+ public int test2(int k) { return 0; }
+ }
+
+ public static class T8c {
+ public int test2(int k) {
+ int jj = 50;
+ if (k > 0)
+ k += "fooo".length();
+
+ int j = 50;
+ loop: for (int i = 0; i < 10; i++) {
+ int jjj = 1;
+ switch (i) {
+ case 0:
+ k++;
+ { int poi = 0; }
+ break;
+ case 9:
+ break loop;
+ case 7:
+ k += jjj;
+ break;
+ }
+ }
+ return j + jj;
+ }
+ }
+
+ public void testSwitchCase4() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T8e");
+ StringBuffer sbuf = new StringBuffer("String s;");
+ for (int i = 0; i < 130; i++)
+ sbuf.append("s =\"" + i + "\";");
+
+ cc.getDeclaredMethod("foo").insertBefore(sbuf.toString());
+ CtClass orig = loader.get("javassist.bytecode.StackMapTest$T8d");
+ CtMethod origM = orig.getDeclaredMethod("test2");
+ writeLdcw(origM);
+ cc.getDeclaredMethod("test2").setBody(origM, null);
+
+ orig.writeFile();
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(100, invoke(t1, "test"));
+ }
+
+ private void writeLdcw(CtMethod method) {
+ CodeAttribute ca = method.getMethodInfo().getCodeAttribute();
+ int index = ca.getConstPool().addStringInfo("ldcw");
+ CodeIterator ci = ca.iterator();
+ ci.writeByte(Opcode.LDC_W, 14);
+ ci.write16bit(index, 15);
+ ci.writeByte(Opcode.LDC_W, 43);
+ ci.write16bit(index, 44);
+ }
+
+ public static class T8e {
+ static T8dd helper() { return new T8dd(); }
+ int integer() { return 9; }
+ boolean integer2(String s) { return true; }
+ static void print(String s) {}
+ private void print2(String s) {}
+ private Object func(String s) { return null; }
+ I8d func2(String s) { return null; }
+ static String ldcw() { return "k"; }
+ static boolean debug = false;
+ void log(String p1,String p2,String p3,Long p4, Object p5, Object p6) {}
+
+ public int foo() { return 1; }
+ public int test() {
+ try {
+ return test2("", 10) ? 1 : 0;
+ } catch (Exception e) {}
+ return 100;
+ }
+ public boolean test2(String s, int k) throws Exception { return false; }
+ }
+
+ public static interface I8d {
+ String foo(String s, int i);
+ }
+
+ public static class T8dd {
+ java.util.List foo(String s) { return new java.util.ArrayList(); }
+ }
+
+ public static class T8d {
+ static T8dd helper() { return new T8dd(); }
+ int integer() { return 9; }
+ boolean integer2(String s) { return true; }
+ static void print(String s) {}
+ private void print2(String s) {}
+ private Object func(String s) { return null; }
+ I8d func2(String s) { return null; }
+ static String ldcw() { return "k"; }
+ static boolean debug = false;
+ void log(String p1,String p2,String p3,Long p4, Object p5, Object p6) {}
+
+ public boolean test2(String s, int i) throws Exception {
+ String k = null;
+ Object k2 = null;
+ boolean v5 = true;
+ if (!debug)
+ print(ldcw());
+
+ Object k3 = this.func(s);
+ if (k3 == null)
+ throw new Exception(new StringBuilder().append(ldcw()).append(s).append(",").toString());
+
+ String v7 = k3.toString();
+ k2 = this.func2(v7);
+ if (k2 != null) { // 82:
+ if (k2 instanceof I8d) {
+ I8d k5 = (I8d)k2;
+ k = k5.foo(s, i);
+ }
+ }
+
+ java.util.List list = helper().foo(v7); // local 8
+ for (int v9 = 0; v9 < list.size(); v9++) {
+ boolean v10 = true;
+ T8d v11 = (T8d)list.get(v9);
+ switch (v11.integer()) {
+ case 1:
+ break;
+ case 2:
+ v10 = this.integer2(s);
+ v5 &= v10;
+ break;
+ default :
+ throw new Exception(new StringBuilder().append("ldc 189").append(v11.integer()).append("ldc 169").toString());
+ }
+ }
+
+ if (v5) // 246:
+ this.print2(s);
+ if (v5)
+ this.log(ldcw(), v7, s, Long.valueOf(Integer.valueOf(i).longValue()), k, null);
+ else // 290:
+ this.log(ldcw(), v7, s, Long.valueOf(Integer.valueOf(i).longValue()), k, null);
+
+ return v5;
+ }
+ }
+
+ public void testInnerClass() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T9");
+ CtClass par = loader.get("javassist.bytecode.StackMapTest$T9$Parent");
+ CtClass in = loader.get("javassist.bytecode.StackMapTest$T9$In");
+ rebuildStackMaps2(cc);
+ rebuildStackMaps2(par);
+ rebuildStackMaps2(in);
+ cc.writeFile();
+ in.writeFile();
+ par.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(19, invoke(t1, "test"));
+ }
+
+ public static class T9 {
+ class Parent {
+ int f;
+ Parent(int i) { f = i; }
+ }
+ class In extends Parent {
+ int value;
+ public In(int i) {
+ super(i > 0 ? 10 : 20);
+ value = i;
+ }
+ }
+
+ public int test() {
+ In in = new In(9);
+ return in.value + in.f;
+ }
+ }
+
+ public void testConstructor3() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$C4");
+ MethodInfo mi = cc.getDeclaredMethod("foo").getMethodInfo();
+ mi.rebuildStackMapForME(loader);
+ CodeIterator ci = mi.getCodeAttribute().iterator();
+ ci.insertGap(0, 7);
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(6, invoke(t1, "test"));
+ }
+
+ public static class C4 {
+ public int test() { return foo(3); }
+ public int foo(int i) {
+ String s = new String(i > 0 ? "pos" : "negative");
+ System.out.println("2nd stage");
+ int len = 0;
+ if (i > 0) {
+ String t = new String(i > 0 ? "pos" : "negative");
+ len = t.length();
+ }
+ return s.length() + len;
+ }
+ }
+
+ public void testJIRA175() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$C5");
+ cc.getDeclaredMethod("setter").instrument(new javassist.expr.ExprEditor() {
+ @Override
+ public void edit(javassist.expr.FieldAccess f) throws javassist.CannotCompileException {
+ if (!f.where().getMethodInfo().isMethod())
+ return;
+
+ f.replace("{ $_ = $proceed($$); if (false) return $_;}");
+ }
+ });
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(3, invoke(t1, "test"));
+ }
+
+ public static class C5 {
+ String value;
+ int ivalue;
+ public int test() {
+ setter("foo");
+ return value.length();
+ }
+
+ public void setter(String s) {
+ value = s;
+ ivalue = s.length();
+ }
+ }
+
+ public void testJIRA175b() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$C6");
+ cc.getDeclaredMethod("setter").instrument(new javassist.expr.ExprEditor() {
+ public void edit(javassist.expr.FieldAccess f) throws javassist.CannotCompileException {
+ if (!f.where().getMethodInfo().isMethod())
+ return;
+
+ // this will make a 1-byte dead code.
+ f.replace("{ $_ = $proceed($$); return $_;}");
+ }
+ });
+ cc.writeFile();
+ }
+
+ public static class C6 {
+ String value;
+ int ivalue;
+ public int test() {
+ setter("foo");
+ return value.length();
+ }
+
+ public void setter(String s) {
+ value = s;
+ ivalue = s.length();
+ }
+ }
+
+ public void testForHibernate() throws Exception {
+ ClassFile cf = loader.makeClass("javassist.bytecode.StackMapTestHibTest").getClassFile();
+ ConstPool cp = cf.getConstPool();
+ MethodInfo mi = new MethodInfo(cp, "foo", "()V");
+ Bytecode code = new Bytecode(cp, 0, 0);
+ code.add(Opcode.RETURN);
+ CodeAttribute ca = code.toCodeAttribute();
+ mi.addAttribute(ca);
+ cf.addMethod(mi);
+
+ int pc = 111;
+ int throwableType_index = cp.addClassInfo(Throwable.class.getName());
+
+ StackMapTable.Writer writer = new StackMapTable.Writer(32);
+ int[] localTags = { StackMapTable.OBJECT, StackMapTable.OBJECT, StackMapTable.OBJECT, StackMapTable.INTEGER };
+ int[] localData = { cp.getThisClassInfo(), cp.addClassInfo("java/lang/Object"),
+ cp.addClassInfo("[Ljava/lang/Object;"), 0};
+ int[] stackTags = { StackMapTable.OBJECT };
+ int[] stackData = { throwableType_index };
+ writer.fullFrame(pc, localTags, localData, stackTags, stackData);
+
+ ca.setAttribute(writer.toStackMapTable(cp));
+ cf.write(new java.io.DataOutputStream(new java.io.FileOutputStream("./test-hibernate.class")));
+ }
+
+ public void testCommonSuperclass() throws Exception {
+ CtClass obj = loader.get("java.lang.Object");
+ CtClass objarray = loader.get("java.lang.Object[]");
+
+ CtClass one = loader.get("byte[]");
+ CtClass two = loader.get("byte[][]");
+ assertEquals("java.lang.Object", TypeData.commonSuperClassEx(one, two).getName());
+ assertTrue(one.subtypeOf(obj));
+ assertTrue(two.subtypeOf(obj));
+ assertFalse(one.subtypeOf(objarray));
+ assertTrue(two.subtypeOf(objarray));
+
+ one = loader.get("int[][]");
+ two = loader.get("java.lang.Object[]");
+ assertEquals("java.lang.Object[]", TypeData.commonSuperClassEx(one, two).getName());
+ assertTrue(one.subtypeOf(objarray));
+ assertTrue(two.subtypeOf(objarray));
+
+ one = loader.get("int[]");
+ two = loader.get("java.lang.String");
+ assertEquals("java.lang.Object", TypeData.commonSuperClassEx(one, two).getName());
+ assertTrue(one.subtypeOf(obj));
+ assertTrue(two.subtypeOf(obj));
+ assertFalse(one.subtypeOf(objarray));
+ assertFalse(two.subtypeOf(objarray));
+
+ one = loader.get("int[]");
+ two = loader.get("int[]");
+ assertEquals("int[]", TypeData.commonSuperClassEx(one, two).getName());
+ assertTrue(one.subtypeOf(obj));
+ assertFalse(one.subtypeOf(objarray));
+
+ one = loader.get("int[]");
+ two = loader.get("java.lang.String[]");
+ assertEquals("java.lang.Object", TypeData.commonSuperClassEx(one, two).getName());
+ assertTrue(one.subtypeOf(obj));
+ assertTrue(two.subtypeOf(obj));
+ assertFalse(one.subtypeOf(objarray));
+ assertTrue(two.subtypeOf(objarray));
+
+ one = loader.get("java.lang.String[]");
+ two = loader.get("java.lang.Class[]");
+ assertEquals("java.lang.Object[]", TypeData.commonSuperClassEx(one, two).getName());
+ assertTrue(one.subtypeOf(objarray));
+ assertTrue(two.subtypeOf(objarray));
+ }
+
+ public void testJsr() throws Exception {
+ CtClass cc = loader.makeClass("javassist.bytecode.StackMapTestJsrTest");
+ ClassFile cf = cc.getClassFile();
+ cf.setMajorVersion(ClassFile.JAVA_5);
+ ConstPool cp = cf.getConstPool();
+ MethodInfo mi = new MethodInfo(cp, "test", "()I");
+ mi.setAccessFlags(AccessFlag.PUBLIC);
+ Bytecode code = new Bytecode(cp, 1, 3);
+ code.addIconst(3);
+ code.addIstore(1);
+ code.addIload(1);
+ code.add(Opcode.IFEQ);
+ code.addIndex(6);
+ code.add(Opcode.JSR);
+ code.addIndex(5);
+ code.addIload(1);
+ code.add(Opcode.IRETURN);
+ code.addAstore(2);
+ code.addRet(2);
+ CodeAttribute ca = code.toCodeAttribute();
+ mi.addAttribute(ca);
+ mi.rebuildStackMap(loader);
+ cf.addMethod(mi);
+ cc.addConstructor(CtNewConstructor.make("public StackMapTestJsrTest() {}", cc));
+ cc.addMethod(CtMethod.make("public static void main(String[] args) {"
+ + "new javassist.bytecode.StackMapTestJsrTest().test();"
+ + "}",
+ cc));
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(3, invoke(t1, "test"));
+ }
+
+ public void tstCtClassType() throws Exception {
+ ClassPool cp = ClassPool.getDefault();
+ CtClass cc = cp.get("javassist.CtClassType");
+ MethodInfo minfo = getMethodInfo(cc.getClassFile(), "getFields", "(Ljava/util/ArrayList;Ljavassist/CtClass;)V");
+ rebuildStackMap(cc, cp, minfo);
+ }
+
+ MethodInfo getMethodInfo(ClassFile cf, String name, String desc) {
+ List list = cf.getMethods();
+ Iterator it = list.iterator();
+ while (it.hasNext()) {
+ MethodInfo mi = (MethodInfo)it.next();
+ if (mi.getName().equals(name) && mi.getDescriptor().equals(desc))
+ return mi;
+ }
+
+ return null;
+ }
+}
diff --git a/src/test/javassist/compiler/CodeTest.java b/src/test/javassist/compiler/CodeTest.java
new file mode 100644
index 0000000..0cff553
--- /dev/null
+++ b/src/test/javassist/compiler/CodeTest.java
@@ -0,0 +1,26 @@
+package javassist.compiler;
+
+import java.io.*;
+import javassist.*;
+import javassist.bytecode.*;
+
+public class CodeTest implements TokenId {
+ public static void main(String[] args) throws Exception {
+ ClassPool loader = ClassPool.getDefault();
+
+ CtClass c = loader.get(args[0]);
+
+ String line
+ = new BufferedReader(new InputStreamReader(System.in)).readLine();
+ Bytecode b = new Bytecode(c.getClassFile().getConstPool(), 0, 0);
+
+ Javac jc = new Javac(b, c);
+ CtMember obj = jc.compile(line);
+ if (obj instanceof CtMethod)
+ c.addMethod((CtMethod)obj);
+ else
+ c.addConstructor((CtConstructor)obj);
+
+ c.writeFile();
+ }
+}
diff --git a/src/test/javassist/compiler/CompTest.java b/src/test/javassist/compiler/CompTest.java
new file mode 100644
index 0000000..4154935
--- /dev/null
+++ b/src/test/javassist/compiler/CompTest.java
@@ -0,0 +1,150 @@
+package javassist.compiler;
+
+import junit.framework.*;
+import javassist.*;
+import java.util.*;
+import javassist.compiler.ast.*;
+
+/*
+public class Test{
+
+ public static void main(String[] args) throws Exception {
+ ClassPool pool = ClassPool.getDefault();
+ CtClass cc = pool.get("Print");
+ CtMethod cm = cc.getDeclaredMethod("print");
+ cm.insertBefore("{((Advice)(new
+ HelloAspect().getAdvice().get(0))).print();}");
+ //cm.insertBefore("{new Advice(\"advice\").print();}");
+ pool.write(cc.getName());
+ new Print().print();
+ }
+}
+*/
+
+public class CompTest extends TestCase {
+ ClassPool sloader;
+
+ public CompTest(String name) {
+ super(name);
+ }
+
+ protected void print(String msg) {
+ System.out.println(msg);
+ }
+
+ protected void setUp() throws Exception {
+ sloader = ClassPool.getDefault();
+ }
+
+ public void testCast() throws Exception {
+ Javac jc = new Javac(sloader.get("javassist.compiler.Print"));
+ jc.compileStmnt(
+ "{((javassist.compiler.Advice)"
+ + " (new javassist.compiler.HelloAspect().getAdvice().get(0)))"
+ + " .print();}");
+ }
+
+ public void testStaticMember() throws Exception {
+ String src = "javassist.compiler.Print#k = 3;";
+ Parser p = new Parser(new Lex(src));
+ SymbolTable stb = new SymbolTable();
+ Stmnt s = p.parseStatement(stb);
+ Expr expr = (Expr)s.getLeft().getLeft();
+ assertEquals('#', expr.getOperator());
+ assertEquals("javassist.compiler.Print",
+ ((Symbol)expr.oprand1()).get());
+ }
+
+ public void testStaticMember2() throws Exception {
+ String src = "String#k = 3;";
+ Parser p = new Parser(new Lex(src));
+ SymbolTable stb = new SymbolTable();
+ Stmnt s = p.parseStatement(stb);
+ Expr expr = (Expr)s.getLeft().getLeft();
+ assertEquals('#', expr.getOperator());
+ assertEquals("String", ((Symbol)expr.oprand1()).get());
+ }
+
+ public void testDoubleConst() {
+ Lex lex = new Lex("7d 0.3d 5e-2d .3d 3e2; .4D 2e-1D;");
+ assertEquals(TokenId.DoubleConstant, lex.get());
+ assertEquals(TokenId.DoubleConstant, lex.get());
+ assertEquals(TokenId.DoubleConstant, lex.get());
+ assertEquals(TokenId.DoubleConstant, lex.get());
+ assertEquals(TokenId.DoubleConstant, lex.get());
+ assertEquals(';', lex.get());
+ assertEquals(TokenId.DoubleConstant, lex.get());
+ assertEquals(TokenId.DoubleConstant, lex.get());
+ assertEquals(';', lex.get());
+ }
+
+ public void testRecordLocalVar() throws Exception {
+ Javac jv = new Javac(sloader.get("javassist.compiler.Print"));
+ jv.gen.recordVariable("I", "i0", 0, jv.stable);
+ isRightDecl((Declarator)jv.stable.get("i0"), TokenId.INT, 0, null);
+ jv.gen.recordVariable("[I", "i1", 1, jv.stable);
+ isRightDecl((Declarator)jv.stable.get("i1"), TokenId.INT, 1, null);
+ jv.gen.recordVariable("[[D", "i2", 2, jv.stable);
+ isRightDecl((Declarator)jv.stable.get("i2"), TokenId.DOUBLE, 2, null);
+ jv.gen.recordVariable("Ljava/lang/String;", "i3", 4, jv.stable);
+ isRightDecl((Declarator)jv.stable.get("i3"), TokenId.CLASS, 0,
+ "java/lang/String");
+ jv.gen.recordVariable("[LTest;", "i4", 5, jv.stable);
+ isRightDecl((Declarator)jv.stable.get("i4"), TokenId.CLASS, 1,
+ "Test");
+ jv.gen.recordVariable("[[LTest;", "i5", 6, jv.stable);
+ isRightDecl((Declarator)jv.stable.get("i5"), TokenId.CLASS, 2,
+ "Test");
+ }
+
+ private void isRightDecl(Declarator d, int type, int dim, String cname) {
+ assertEquals(type, d.getType());
+ assertEquals(dim, d.getArrayDim());
+ assertEquals(cname, d.getClassName());
+ }
+
+ public void testArgTypesToString() {
+ String s;
+ s = TypeChecker.argTypesToString(new int[0], new int[0], new String[0]);
+ assertEquals("()", s);
+ s = TypeChecker.argTypesToString(new int[] { TokenId.INT, TokenId.CHAR, TokenId.CLASS },
+ new int[] { 0, 1, 0 },
+ new String[] { null, null, "String" });
+ assertEquals("(int,char[],String)", s);
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Compiler Tests");
+ suite.addTestSuite(CompTest.class);
+ return suite;
+ }
+}
+
+class Print{
+ public void print(){ System.out.println("@@@"); }
+ public static int k;
+}
+
+@SuppressWarnings({"rawtypes","unchecked"})
+class HelloAspect{
+ List list;
+
+ HelloAspect() {
+ list = new LinkedList();
+ list.add(new Advice("advice"));
+ }
+
+ List getAdvice() {
+ return list;
+ }
+}
+
+class Advice{
+ String str = "";
+ Advice(String str) {
+ this.str = str;
+ }
+ void print(){
+ System.out.println(str);
+ }
+}
diff --git a/src/test/javassist/compiler/LexTest.java b/src/test/javassist/compiler/LexTest.java
new file mode 100644
index 0000000..a02cd5f
--- /dev/null
+++ b/src/test/javassist/compiler/LexTest.java
@@ -0,0 +1,21 @@
+package javassist.compiler;
+
+public class LexTest implements TokenId {
+ public static void main(String[] args) {
+ Lex lex = new Lex(args[0]);
+ int t;
+ while ((t = lex.get()) > 0) {
+ System.out.print(t);
+ if (t == Identifier || t == StringL)
+ System.out.print("(" + lex.getString() + ") ");
+ else if (t == CharConstant || t == IntConstant)
+ System.out.print("(" + lex.getLong() + ") ");
+ else if (t == DoubleConstant)
+ System.out.print("(" + lex.getDouble() + ") ");
+ else
+ System.out.print(" ");
+ }
+
+ System.out.println(" ");
+ }
+}
diff --git a/src/test/javassist/compiler/ParseTest.java b/src/test/javassist/compiler/ParseTest.java
new file mode 100644
index 0000000..ad51155
--- /dev/null
+++ b/src/test/javassist/compiler/ParseTest.java
@@ -0,0 +1,13 @@
+package javassist.compiler;
+
+import javassist.compiler.ast.*;
+
+public class ParseTest implements TokenId {
+ public static void main(String[] args) throws CompileError {
+ Parser p = new Parser(new Lex(args[0]));
+ SymbolTable stb = new SymbolTable();
+ // MethodDecl s = (MethodDecl)p.parseMember(stb);
+ Stmnt s = p.parseStatement(stb);
+ System.out.println(s == null ? "null" : s.toString());
+ }
+}
diff --git a/src/test/javassist/proxyfactory/MyCls.java b/src/test/javassist/proxyfactory/MyCls.java
new file mode 100644
index 0000000..c4c5f35
--- /dev/null
+++ b/src/test/javassist/proxyfactory/MyCls.java
@@ -0,0 +1,29 @@
+package javassist.proxyfactory;
+
+import java.io.Serializable;
+
+/**
+ * <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
+ */
+public class MyCls implements Serializable {
+
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+ private int i,j;
+
+ public int getI() {
+ return i;
+ }
+
+ public void setI(int i) {
+ this.i = i;
+ }
+
+ public int getJ() {
+ return j;
+ }
+
+ public void setJ(int j) {
+ this.j = j;
+ }
+}
diff --git a/src/test/javassist/proxyfactory/ProxyFactoryTest.java b/src/test/javassist/proxyfactory/ProxyFactoryTest.java
new file mode 100644
index 0000000..c69acc9
--- /dev/null
+++ b/src/test/javassist/proxyfactory/ProxyFactoryTest.java
@@ -0,0 +1,155 @@
+package javassist.proxyfactory;
+
+import javassist.util.proxy.MethodHandler;
+import javassist.util.proxy.ProxyFactory;
+import javassist.util.proxy.ProxyObject;
+import junit.framework.TestCase;
+
+import java.io.*;
+import java.lang.reflect.Method;
+
+/**
+ * <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
+ */
+@SuppressWarnings({"rawtypes","unchecked","unused"})
+public class ProxyFactoryTest extends TestCase {
+ public void testMethodHandlers() throws Exception {
+ ProxyFactory fact = new ProxyFactory();
+ fact.setSuperclass(MyCls.class);
+
+ Class proxyClass = fact.createClass();
+
+ MyMethodHandler myHandler = new MyMethodHandler();
+ myHandler.setX(4711);
+
+ MyCls myCls = (MyCls) proxyClass.getConstructor().newInstance();
+ ((ProxyObject) myCls).setHandler(myHandler);
+
+ MethodHandler h2 = ((ProxyObject) myCls).getHandler();
+ assertNotNull(h2);
+ assertTrue(h2 instanceof MyMethodHandler);
+ }
+
+ public void testSerialize() throws Exception {
+ ProxyFactory fact = new ProxyFactory();
+ fact.setSuperclass(MyCls.class);
+
+ Class proxyClass = fact.createClass();
+
+ MyMethodHandler myHandler = new MyMethodHandler();
+ myHandler.setX(4711);
+
+ MyCls myCls = (MyCls) proxyClass.getConstructor().newInstance();
+ ((ProxyObject) myCls).setHandler(myHandler);
+
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(myCls);
+ byte[] ba = baos.toByteArray();
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(ba);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ MyCls myCls2 = (MyCls) ois.readObject();
+
+ MethodHandler h2 = ((ProxyObject) myCls2).getHandler();
+ assertNotNull(h2);
+ assertTrue(h2 instanceof MyMethodHandler);
+ }
+
+
+ public static class MyMethodHandler implements MethodHandler, Serializable {
+
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+ private int x;
+
+ public int getX() {
+ return x;
+ }
+
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
+ // actually do nothing!
+ return null;
+ }
+ }
+
+ public void testJira127() throws Exception {
+ ProxyFactory proxyFactory = new ProxyFactory();
+ proxyFactory.setInterfaces(new Class[]{ JIRA127Sub.class });
+ proxyFactory.createClass();
+ }
+
+ public interface JIRA127 {
+ JIRA127 get();
+ }
+ public interface JIRA127Sub extends JIRA127 {
+ JIRA127Sub get();
+ }
+
+ public void testDefaultMethod() throws Exception {
+ ProxyFactory proxyFactory = new ProxyFactory();
+ //proxyFactory.writeDirectory = "./dump";
+ proxyFactory.setInterfaces(new Class[]{ TestDefaultI.class });
+ Class intf = proxyFactory.createClass();
+ TestDefaultI obj = (TestDefaultI)intf.getConstructor().newInstance();
+ obj.foo();
+
+ ProxyFactory proxyFactory2 = new ProxyFactory();
+ //proxyFactory2.writeDirectory = "./dump";
+ proxyFactory2.setSuperclass(TestDefaultC.class);
+ Class clazz2 = proxyFactory2.createClass();
+ TestDefaultC obj2 = (TestDefaultC)clazz2.getConstructor().newInstance();
+ obj2.foo();
+ obj2.bar();
+
+ ProxyFactory proxyFactory3 = new ProxyFactory();
+ proxyFactory3.setSuperclass(TestDefaultC2.class);
+ Class clazz3 = proxyFactory3.createClass();
+ TestDefaultC2 obj3 = (TestDefaultC2)clazz3.getConstructor().newInstance();
+ obj3.foo();
+ obj3.bar();
+ obj3.baz();
+ }
+
+ public static interface TestDefaultI {
+ default int foo() { return 10; }
+ }
+
+ public static class TestDefaultC implements TestDefaultI {
+ public int foo() { return 1; }
+ public int bar() { return TestDefaultI.super.foo(); }
+ }
+
+ public static class TestDefaultC2 extends TestDefaultC {
+ public int baz() { return super.foo(); }
+ }
+
+ public void testJava11() throws Exception {
+ ProxyFactory factory = new ProxyFactory();
+ factory.setSuperclass(java.util.HashMap.class);
+ java.util.HashMap e = (java.util.HashMap)factory.create(null, null, new MethodHandler() {
+ @Override
+ public Object invoke(Object self, Method thisMethod,
+ Method proceed, Object[] args) throws Throwable {
+ return proceed.invoke(self, args);
+ }
+ });
+ }
+
+ public void testJava11jdk() throws Exception {
+ ProxyFactory factory = new ProxyFactory();
+ factory.setSuperclass(jdk.javadoc.doclet.StandardDoclet.class);
+ jdk.javadoc.doclet.StandardDoclet e = (jdk.javadoc.doclet.StandardDoclet)factory.create(null, null, new MethodHandler() {
+ @Override
+ public Object invoke(Object self, Method thisMethod,
+ Method proceed, Object[] args) throws Throwable {
+ return proceed.invoke(self, args);
+ }
+ });
+ }
+}
diff --git a/src/test/javassist/proxyfactory/Tester.java b/src/test/javassist/proxyfactory/Tester.java
new file mode 100644
index 0000000..874fba1
--- /dev/null
+++ b/src/test/javassist/proxyfactory/Tester.java
@@ -0,0 +1,58 @@
+package javassist.proxyfactory;
+
+import junit.framework.*;
+import javassist.util.proxy.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.lang.reflect.Method;
+
+class Hand implements java.io.Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+ public int setHandler(int i) { return i; }
+ int getHandler() { return 3; }
+}
+
+@SuppressWarnings({"rawtypes","unchecked","resource"})
+public class Tester extends TestCase {
+ static class MHandler implements MethodHandler, java.io.Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ public Object invoke(Object self, Method m, Method proceed,
+ Object[] args) throws Throwable {
+ System.out.println("Name: " + m.getName());
+ return proceed.invoke(self, args);
+ }
+ }
+
+ static MethodHandler mi = new MHandler();
+
+ public void test() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.setSuperclass(Hand.class);
+ Class c = f.createClass();
+ Hand foo = (Hand)c.getConstructor().newInstance();
+ ((Proxy)foo).setHandler(mi);
+ assertTrue(ProxyFactory.isProxyClass(c));
+ assertEquals(3, foo.getHandler());
+ }
+
+ public void test2() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.setSuperclass(Hand.class);
+ Hand h = (Hand)f.create(new Class[0], new Object[0], mi);
+ assertEquals(3, h.getHandler());
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ProxyObjectOutputStream out = new ProxyObjectOutputStream(bos);
+ out.writeObject(h);
+ out.close();
+ byte[] bytes = bos.toByteArray();
+ ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+ ProxyObjectInputStream in = new ProxyObjectInputStream(bis);
+ Hand h2 = (Hand)in.readObject();
+ assertEquals(3, h2.getHandler());
+ }
+}
diff --git a/src/test/javassist/tools/CallbackTest.java b/src/test/javassist/tools/CallbackTest.java
new file mode 100644
index 0000000..f5861ce
--- /dev/null
+++ b/src/test/javassist/tools/CallbackTest.java
@@ -0,0 +1,41 @@
+package javassist.tools;
+
+import javassist.*;
+import junit.framework.TestCase;
+import test.javassist.tools.DummyClass;
+import test.javassist.tools.DefineClassCapability;
+
+import static javassist.tools.Callback.*;
+
+public class CallbackTest extends TestCase {
+
+ public void testSomeCallbacks() throws Exception {
+
+ // Get class and method to change
+ ClassPool pool = ClassPool.getDefault();
+ CtClass classToChange = pool.get("test.javassist.tools.DummyClass");
+ CtMethod methodToChange = classToChange.getDeclaredMethod("dummyMethod");
+
+ // Insert after
+ methodToChange.insertAfter(new Callback("Thread.currentThread(), dummyString") {
+ @Override
+ public void result(Object... objects) {
+ assertEquals(objects[0], Thread.currentThread());
+ assertEquals(objects[1], "dummyStringValue");
+ }
+ }.sourceCode());
+
+ // Insert after using utility method
+ insertAfter(methodToChange, new Callback("Thread.currentThread(), dummyString") {
+ @Override
+ public void result(Object... objects) {
+ assertEquals(objects[0], Thread.currentThread());
+ assertEquals(objects[1], "dummyStringValue");
+ }
+ });
+
+ // Change class and invoke method;
+ classToChange.toClass(DefineClassCapability.class);
+ new DummyClass().dummyMethod();
+ }
+}
diff --git a/src/test/javassist/tools/reflect/ClassMetaobjectTest.java b/src/test/javassist/tools/reflect/ClassMetaobjectTest.java
new file mode 100644
index 0000000..904fbaf
--- /dev/null
+++ b/src/test/javassist/tools/reflect/ClassMetaobjectTest.java
@@ -0,0 +1,14 @@
+package javassist.tools.reflect;
+
+/**
+ * @author Brett Randall
+ */
+public class ClassMetaobjectTest {
+ public static void main(String[] args) throws Throwable {
+ Loader loader = new Loader();
+ loader.makeReflective("javassist.tools.reflect.Person",
+ "javassist.tools.reflect.Metaobject",
+ "javassist.tools.reflect.ClassMetaobject");
+ loader.run("javassist.tools.reflect.Person", new String[] {});
+ }
+}
diff --git a/src/test/javassist/tools/reflect/LoaderTest.java b/src/test/javassist/tools/reflect/LoaderTest.java
new file mode 100644
index 0000000..f96feb7
--- /dev/null
+++ b/src/test/javassist/tools/reflect/LoaderTest.java
@@ -0,0 +1,73 @@
+package javassist.tools.reflect;
+
+import javassist.*;
+import junit.framework.*;
+
+public class LoaderTest extends TestCase {
+ private Loader loader;
+
+ public LoaderTest(String name) {
+ super(name);
+ }
+
+ public void setUp() throws Exception {
+ loader = new Loader();
+ }
+
+ public void testAttemptReflectInterface() throws Exception {
+ try {
+ loader.makeReflective("javassist.ClassPath",
+ "javassist.tools.reflect.Metaobject",
+ "javassist.tools.reflect.ClassMetaobject");
+ fail("Attempting to reflect an interface should throw a CannotReflectException");
+ } catch (CannotReflectException e) {
+ // expected
+ }
+ }
+
+ public void testAttemptReflectClassMetaobject() throws Exception {
+ try {
+ loader.makeReflective("javassist.tools.reflect.ClassMetaobject",
+ "javassist.tools.reflect.Metaobject",
+ "javassist.tools.reflect.ClassMetaobject");
+ fail("Attempting to reflect a ClassMetaobject should throw a CannotReflectException");
+ } catch (CannotReflectException e) {
+ // expected
+ }
+ }
+
+ public void testAttemptReflectMetaobject() throws Exception {
+ try {
+ loader.makeReflective("javassist.tools.reflect.Metaobject",
+ "javassist.tools.reflect.Metaobject",
+ "javassist.tools.reflect.ClassMetaobject");
+ fail("Attempting to reflect a Metaobject should throw a CannotReflectException");
+ } catch (CannotReflectException e) {
+ // expected
+ }
+ }
+
+ public void testFinalMethod() throws Throwable {
+ loader.makeReflective("javassist.tools.reflect.SuperClass",
+ "javassist.tools.reflect.Metaobject",
+ "javassist.tools.reflect.ClassMetaobject");
+
+ ClassPool cp = ClassPool.getDefault();
+
+ CtClass cc2 = cp.get("javassist.tools.reflect.SuperClass");
+ cc2.debugWriteFile("reflected/");
+
+ CtClass cc = cp.get("javassist.tools.reflect.SubClass");
+
+ CtMethod[] ms = cc.getMethods();
+ for (int i = 0; i < ms.length; ++i)
+ System.out.println(ms[i] + " in "
+ + ms[i].getDeclaringClass().getName());
+
+ loader.makeReflective("javassist.tools.reflect.SubClass",
+ "javassist.tools.reflect.Metaobject",
+ "javassist.tools.reflect.ClassMetaobject");
+
+ loader.run("javassist.tools.reflect.SubClass", new String[] {});
+ }
+}
diff --git a/src/test/javassist/tools/reflect/Person.java b/src/test/javassist/tools/reflect/Person.java
new file mode 100644
index 0000000..c9e061c
--- /dev/null
+++ b/src/test/javassist/tools/reflect/Person.java
@@ -0,0 +1,64 @@
+package javassist.tools.reflect;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import org.junit.Assert;
+
+/**
+ * Work with ClassMetaobjectTest.java
+ *
+ * @author Brett Randall
+ */
+public class Person {
+ public String name;
+ public static int birth = 3;
+ public static final String defaultName = "John";
+
+ public Person(String name, int birthYear) {
+ if (name == null)
+ this.name = defaultName;
+ else
+ this.name = name;
+
+ Person.birth = birthYear;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge(int year) {
+ return year - birth;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Person person = new Person("Bob", 10);
+
+ Metalevel metalevel = (Metalevel) person;
+ ClassMetaobject cmo = metalevel._getClass();
+
+ // this should return the index of the original getAge method
+ int methodIndex = cmo.getMethodIndex("getAge",
+ new Class[] {Integer.TYPE});
+
+ System.out.println(methodIndex);
+
+ // and the name verified by getMethodName
+ String methodName = cmo.getMethodName(methodIndex);
+ System.out.println(methodName);
+
+ // check the name
+ Assert.assertEquals("Incorrect method was found",
+ "getAge", methodName);
+
+ // check the arguments
+ Method method = cmo.getReflectiveMethods()[methodIndex];
+ Assert.assertTrue("Method signature did not match",
+ Arrays.equals(method.getParameterTypes(),
+ new Class[] {Integer.TYPE}));
+ System.out.println(method);
+ System.out.println("OK");
+ }
+}
+
diff --git a/src/test/javassist/tools/reflect/SubClass.java b/src/test/javassist/tools/reflect/SubClass.java
new file mode 100644
index 0000000..cf42f77
--- /dev/null
+++ b/src/test/javassist/tools/reflect/SubClass.java
@@ -0,0 +1,20 @@
+package javassist.tools.reflect;
+
+import org.junit.Assert;
+
+public class SubClass extends SuperClass {
+ public String f() { return "f2"; } // override
+ public String i() { return "i"; }
+ public final String j() { return "j"; }
+
+ public static void main(String[] args) {
+ SuperClass sup = new SuperClass();
+ SubClass sub = new SubClass();
+ String s = sup.f() + sup.g() + sup.h();
+ String t = sub.f() + sub.g() + sub.h() + sub.i() + sub.j();
+ System.out.println(s);
+ System.out.println(t);
+ Assert.assertEquals("fgh", s);
+ Assert.assertEquals("f2ghij", t);
+ }
+}
diff --git a/src/test/javassist/tools/reflect/SuperClass.java b/src/test/javassist/tools/reflect/SuperClass.java
new file mode 100644
index 0000000..5547c0d
--- /dev/null
+++ b/src/test/javassist/tools/reflect/SuperClass.java
@@ -0,0 +1,7 @@
+package javassist.tools.reflect;
+
+public class SuperClass {
+ public String f() { return "f"; }
+ public String g() { return "g"; }
+ public final String h() { return "h"; }
+}
diff --git a/src/test/resources/Readme.txt b/src/test/resources/Readme.txt
new file mode 100644
index 0000000..77790dc
--- /dev/null
+++ b/src/test/resources/Readme.txt
@@ -0,0 +1,16 @@
+This directory contains files used by the unit tests.
+
+empty.jar:
+An empty, but valid, jar file.
+
+simple.jar:
+Contains a single Java class
+
+```
+package com.test;
+
+public class Test {
+ public Test() {
+ }
+}
+```
diff --git a/src/test/resources/empty.jar b/src/test/resources/empty.jar
new file mode 100644
index 0000000..3a12dba
--- /dev/null
+++ b/src/test/resources/empty.jar
Binary files differ
diff --git a/src/test/resources/simple.jar b/src/test/resources/simple.jar
new file mode 100644
index 0000000..e9455b9
--- /dev/null
+++ b/src/test/resources/simple.jar
Binary files differ
diff --git a/src/test/scoped/ScopedRepositoryTestCase.java b/src/test/scoped/ScopedRepositoryTestCase.java
new file mode 100644
index 0000000..9dca4f5
--- /dev/null
+++ b/src/test/scoped/ScopedRepositoryTestCase.java
@@ -0,0 +1,242 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+package scoped;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.stream.LongStream;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.scopedpool.ScopedClassPool;
+import javassist.scopedpool.ScopedClassPoolRepository;
+import javassist.scopedpool.ScopedClassPoolRepositoryImpl;
+import javassist.scopedpool.SoftValueHashMap;
+import junit.framework.TestCase;
+
+
+/**
+ * ScopedRepositoryTest.
+ *
+ * @author <a href="adrian@jboss.com">Adrian Brock</a>
+ * @version $Revision$
+ */
+public class ScopedRepositoryTestCase extends TestCase
+{
+ private static final ScopedClassPoolRepository repository = ScopedClassPoolRepositoryImpl.getInstance();
+
+ public void testJDKClasses() throws Exception
+ {
+ ClassPool poolClass = repository.findClassPool(Class.class.getClassLoader());
+ assertNotNull(poolClass);
+ ClassPool poolString = repository.findClassPool(String.class.getClassLoader());
+ assertNotNull(poolString);
+ assertEquals(poolClass, poolString);
+ }
+
+ public void testScopedClasses() throws Exception
+ {
+ ClassLoader cl = getURLClassLoader("test-classes14-jar1");
+ ClassPool pool1 = repository.findClassPool(cl);
+ CtClass clazz = pool1.get("scoped.jar1.TestClass1");
+ assertNotNull(clazz);
+ ClassPool poolClass = repository.findClassPool(Class.class.getClassLoader());
+ assertNotNull(poolClass);
+ assertNotSame(pool1, poolClass);
+ }
+
+ public void testUnscopedAnnotationUsage() throws Exception
+ {
+ CtClass clazz = getCtClass(UnscopedAnnotationUsage.class);
+ checkTestAnnotation(clazz, "notDefault");
+ }
+
+ public void testUnscopedAnnotationDefaultUsage() throws Exception
+ {
+ CtClass clazz = getCtClass(UnscopedAnnotationDefaultUsage.class);
+ checkTestAnnotation(clazz, "defaultValue");
+ }
+
+ public void testScopedAnnotationUsage() throws Exception
+ {
+ ClassLoader cl = getURLClassLoader("test-classes14-jar1");
+ CtClass clazz = getCtClass("scoped.jar1.ScopedAnnotationUsage", cl);
+ checkTestAnnotation(clazz, "notDefault");
+ }
+
+ public void testScopedAnnotationDefaultUsage() throws Exception
+ {
+ ClassLoader cl = getURLClassLoader("test-classes14-jar1");
+ CtClass clazz = getCtClass("scoped.jar1.ScopedAnnotationDefaultUsage", cl);
+ checkTestAnnotation(clazz, "defaultValue");
+ }
+
+ public void testFullyScopedAnnotationUsage() throws Exception
+ {
+ ClassLoader cl = getURLClassLoader("test-classes14-jar1");
+ CtClass clazz = getCtClass("scoped.jar1.FullyScopedAnnotationUsage", cl);
+ checkScopedAnnotation(cl, clazz, "notDefault");
+ }
+
+ public void testFullyScopedAnnotationDefaultUsage() throws Exception
+ {
+ ClassLoader cl = getURLClassLoader("test-classes14-jar1");
+ CtClass clazz = getCtClass("scoped.jar1.FullyScopedAnnotationDefaultUsage", cl);
+ checkScopedAnnotation(cl, clazz, "defaultValue");
+ }
+
+ public void testSoftValueHashMap() throws Exception {
+ Map<String,Class<?>> map = new SoftValueHashMap<>();
+ Class<?> cls = this.getClass();
+ assertTrue(map.put(cls.getName(), cls) == null);
+ assertTrue(map.put(cls.getName(), cls) == cls);
+ assertTrue(map.size() == 1);
+ assertTrue(map.get(cls.getName()) == cls);
+ assertTrue(map.values().iterator().next() == cls);
+ assertTrue(map.entrySet().iterator().next().getValue() == cls);
+ assertTrue(map.containsValue(cls));
+ assertTrue(map.remove(cls.getName()) == cls);
+ assertTrue(map.size() == 0);
+ }
+
+ public void testSoftCache() throws Exception {
+ // Overload the heap to test that the map auto cleans
+ Map<String,long[]> map = new SoftValueHashMap<>();
+ // 12+8*30000000 = +- 252 MB
+ long[] data = LongStream.range(0, 30000000).toArray();
+ int current = map.size();
+ while (current <= map.size()) {
+ current = map.size();
+ for (int ii = 0; ii < 5; ii++) {
+ map.put(current+"-"+ii, Arrays.copyOf(data, data.length));
+ }
+ }
+ assertTrue(current > map.size());
+ }
+
+ protected CtClass getCtClass(Class<?> clazz) throws Exception
+ {
+ return getCtClass(clazz.getName(), clazz.getClassLoader());
+ }
+
+ protected CtClass getCtClass(String name, ClassLoader cl) throws Exception
+ {
+ ClassPool pool = repository.findClassPool(cl);
+ assertNotNull(pool);
+ CtClass clazz = pool.get(name);
+ assertNotNull(clazz);
+ return clazz;
+ }
+
+ protected void checkTestAnnotation(CtClass ctClass, String value) throws Exception
+ {
+ checkTestAnnotation(ctClass.getAnnotations(), value);
+ checkTestAnnotation(getFieldAnnotations(ctClass), value);
+ checkTestAnnotation(getConstructorAnnotations(ctClass), value);
+ checkTestAnnotation(getConstructorParameterAnnotations(ctClass), value);
+ checkTestAnnotation(getMethodAnnotations(ctClass), value);
+ checkTestAnnotation(getMethodParameterAnnotations(ctClass), value);
+ }
+
+ protected void checkTestAnnotation(Object[] annotations, String value) throws Exception
+ {
+ assertNotNull(annotations);
+ assertEquals(1, annotations.length);
+ assertNotNull(annotations[0]);
+ assertTrue(annotations[0] instanceof TestAnnotation);
+ TestAnnotation annotation = (TestAnnotation) annotations[0];
+ assertEquals(value, annotation.something());
+ }
+
+ protected void checkScopedAnnotation(ClassLoader cl, CtClass ctClass, String value) throws Exception
+ {
+ Class<?> annotationClass = cl.loadClass("scoped.jar1.ScopedTestAnnotation");
+ checkScopedAnnotation(annotationClass, ctClass.getAnnotations(), value);
+ checkScopedAnnotation(annotationClass, getFieldAnnotations(ctClass), value);
+ checkScopedAnnotation(annotationClass, getConstructorAnnotations(ctClass), value);
+ checkScopedAnnotation(annotationClass, getConstructorParameterAnnotations(ctClass), value);
+ checkScopedAnnotation(annotationClass, getMethodAnnotations(ctClass), value);
+ checkScopedAnnotation(annotationClass, getMethodParameterAnnotations(ctClass), value);
+ }
+
+ protected void checkScopedAnnotation(Class<?> annotationClass, Object[] annotations, String value) throws Exception
+ {
+ assertNotNull(annotations);
+ assertEquals(1, annotations.length);
+ assertNotNull(annotations[0]);
+ assertTrue(annotationClass.isInstance(annotations[0]));
+
+ Method method = annotationClass.getMethod("something", new Class<?>[0]);
+ assertEquals(value, method.invoke(annotations[0], (Object[]) null));
+ }
+
+ protected Object[] getFieldAnnotations(CtClass clazz) throws Exception
+ {
+ CtField field = clazz.getField("aField");
+ assertNotNull(field);
+ return field.getAnnotations();
+ }
+
+ protected Object[] getMethodAnnotations(CtClass clazz) throws Exception
+ {
+ CtMethod method = clazz.getMethod("doSomething", "(I)V");
+ assertNotNull(method);
+ return method.getAnnotations();
+ }
+
+ protected Object[] getMethodParameterAnnotations(CtClass clazz) throws Exception
+ {
+ CtMethod method = clazz.getMethod("doSomething", "(I)V");
+ assertNotNull(method);
+ Object[] paramAnnotations = method.getParameterAnnotations();
+ assertNotNull(paramAnnotations);
+ assertEquals(1, paramAnnotations.length);
+ return (Object[]) paramAnnotations[0];
+ }
+
+ protected Object[] getConstructorAnnotations(CtClass clazz) throws Exception
+ {
+ CtConstructor constructor = clazz.getConstructor("(I)V");
+ assertNotNull(constructor);
+ return constructor.getAnnotations();
+ }
+
+ protected Object[] getConstructorParameterAnnotations(CtClass clazz) throws Exception
+ {
+ CtConstructor constructor = clazz.getConstructor("(I)V");
+ assertNotNull(constructor);
+ Object[] paramAnnotations = constructor.getParameterAnnotations();
+ assertNotNull(paramAnnotations);
+ assertEquals(1, paramAnnotations.length);
+ return (Object[]) paramAnnotations[0];
+ }
+
+ protected ClassLoader getURLClassLoader(String context) throws Exception
+ {
+ String output = ".";
+ File file = new File(output + File.separator + context);
+ URL url = file.toURI().toURL();
+ return new URLClassLoader(new URL[] { url });
+ }
+}
diff --git a/src/test/scoped/TestAnnotation.java b/src/test/scoped/TestAnnotation.java
new file mode 100644
index 0000000..dd5c7b8
--- /dev/null
+++ b/src/test/scoped/TestAnnotation.java
@@ -0,0 +1,34 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+package scoped;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * TestAnnotation.
+ *
+ * @author <a href="adrian@jboss.com">Adrian Brock</a>
+ * @version $Revision$
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR })
+public @interface TestAnnotation
+{
+ String something() default "defaultValue";
+}
diff --git a/src/test/scoped/UnscopedAnnotationDefaultUsage.java b/src/test/scoped/UnscopedAnnotationDefaultUsage.java
new file mode 100644
index 0000000..bcbecfe
--- /dev/null
+++ b/src/test/scoped/UnscopedAnnotationDefaultUsage.java
@@ -0,0 +1,29 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+package scoped;
+
+@TestAnnotation
+public class UnscopedAnnotationDefaultUsage
+{
+ @TestAnnotation
+ public int aField;
+
+ @TestAnnotation
+ public UnscopedAnnotationDefaultUsage(@TestAnnotation int param) {}
+
+ @TestAnnotation
+ public void doSomething(@TestAnnotation int param) {}
+}
diff --git a/src/test/scoped/UnscopedAnnotationUsage.java b/src/test/scoped/UnscopedAnnotationUsage.java
new file mode 100644
index 0000000..e63db33
--- /dev/null
+++ b/src/test/scoped/UnscopedAnnotationUsage.java
@@ -0,0 +1,29 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+package scoped;
+
+@TestAnnotation(something="notDefault")
+public class UnscopedAnnotationUsage
+{
+ @TestAnnotation(something="notDefault")
+ public int aField;
+
+ @TestAnnotation(something="notDefault")
+ public UnscopedAnnotationUsage(@TestAnnotation(something="notDefault") int param) {}
+
+ @TestAnnotation(something="notDefault")
+ public void doSomething(@TestAnnotation(something="notDefault") int param) {}
+}
diff --git a/src/test/scoped/jar1/FullyScopedAnnotationDefaultUsage.java b/src/test/scoped/jar1/FullyScopedAnnotationDefaultUsage.java
new file mode 100644
index 0000000..663d18f
--- /dev/null
+++ b/src/test/scoped/jar1/FullyScopedAnnotationDefaultUsage.java
@@ -0,0 +1,29 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+package scoped.jar1;
+
+@ScopedTestAnnotation
+public class FullyScopedAnnotationDefaultUsage
+{
+ @ScopedTestAnnotation
+ public int aField;
+
+ @ScopedTestAnnotation
+ public FullyScopedAnnotationDefaultUsage(@ScopedTestAnnotation int value) {}
+
+ @ScopedTestAnnotation
+ public void doSomething(@ScopedTestAnnotation int value) {}
+}
diff --git a/src/test/scoped/jar1/FullyScopedAnnotationUsage.java b/src/test/scoped/jar1/FullyScopedAnnotationUsage.java
new file mode 100644
index 0000000..c44aeb7
--- /dev/null
+++ b/src/test/scoped/jar1/FullyScopedAnnotationUsage.java
@@ -0,0 +1,29 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+package scoped.jar1;
+
+@ScopedTestAnnotation(something="notDefault")
+public class FullyScopedAnnotationUsage
+{
+ @ScopedTestAnnotation(something="notDefault")
+ public int aField;
+
+ @ScopedTestAnnotation(something="notDefault")
+ public FullyScopedAnnotationUsage(@ScopedTestAnnotation(something="notDefault") int param) {}
+
+ @ScopedTestAnnotation(something="notDefault")
+ public void doSomething(@ScopedTestAnnotation(something="notDefault") int param) {}
+}
diff --git a/src/test/scoped/jar1/ScopedAnnotationDefaultUsage.java b/src/test/scoped/jar1/ScopedAnnotationDefaultUsage.java
new file mode 100644
index 0000000..284b086
--- /dev/null
+++ b/src/test/scoped/jar1/ScopedAnnotationDefaultUsage.java
@@ -0,0 +1,31 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+package scoped.jar1;
+
+import scoped.TestAnnotation;
+
+@TestAnnotation
+public class ScopedAnnotationDefaultUsage
+{
+ @TestAnnotation
+ public int aField;
+
+ @TestAnnotation
+ public ScopedAnnotationDefaultUsage(@TestAnnotation int param) {}
+
+ @TestAnnotation
+ public void doSomething(@TestAnnotation int param) {}
+}
diff --git a/src/test/scoped/jar1/ScopedAnnotationUsage.java b/src/test/scoped/jar1/ScopedAnnotationUsage.java
new file mode 100644
index 0000000..23400f2
--- /dev/null
+++ b/src/test/scoped/jar1/ScopedAnnotationUsage.java
@@ -0,0 +1,31 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+package scoped.jar1;
+
+import scoped.TestAnnotation;
+
+@TestAnnotation(something="notDefault")
+public class ScopedAnnotationUsage
+{
+ @TestAnnotation(something="notDefault")
+ public int aField;
+
+ @TestAnnotation(something="notDefault")
+ public ScopedAnnotationUsage(@TestAnnotation(something="notDefault") int param) {}
+
+ @TestAnnotation(something="notDefault")
+ public void doSomething(@TestAnnotation(something="notDefault") int param) {}
+}
diff --git a/src/test/scoped/jar1/ScopedTestAnnotation.java b/src/test/scoped/jar1/ScopedTestAnnotation.java
new file mode 100644
index 0000000..25a36a5
--- /dev/null
+++ b/src/test/scoped/jar1/ScopedTestAnnotation.java
@@ -0,0 +1,34 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+package scoped.jar1;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * TestAnnotation.
+ *
+ * @author <a href="adrian@jboss.com">Adrian Brock</a>
+ * @version $Revision$
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR })
+public @interface ScopedTestAnnotation
+{
+ String something() default "defaultValue";
+}
diff --git a/src/test/scoped/jar1/TestClass1.java b/src/test/scoped/jar1/TestClass1.java
new file mode 100644
index 0000000..a538bf7
--- /dev/null
+++ b/src/test/scoped/jar1/TestClass1.java
@@ -0,0 +1,26 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later,
+ * or the Apache License Version 2.0.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+package scoped.jar1;
+
+/**
+ * TestClass1.
+ *
+ * @author <a href="adrian@jboss.com">Adrian Brock</a>
+ * @version $Revision$
+ */
+public class TestClass1
+{
+}
diff --git a/src/test/test/javassist/DefineClassCapability.java b/src/test/test/javassist/DefineClassCapability.java
new file mode 100644
index 0000000..e6a0fd5
--- /dev/null
+++ b/src/test/test/javassist/DefineClassCapability.java
@@ -0,0 +1,7 @@
+package test.javassist;
+
+/*
+ * This is used as a capability for running CtClass#toClass().
+ */
+public class DefineClassCapability {
+}
diff --git a/src/test/test/javassist/bytecode/analysis/AnalyzerTest.java b/src/test/test/javassist/bytecode/analysis/AnalyzerTest.java
index 0c5a77e..b76ff0e 100644
--- a/src/test/test/javassist/bytecode/analysis/AnalyzerTest.java
+++ b/src/test/test/javassist/bytecode/analysis/AnalyzerTest.java
@@ -319,13 +319,17 @@ public class AnalyzerTest extends TestCase {
public static class A {};
- public static class B1 extends A implements Serializable {};
- public static class B2 extends A implements Serializable {};
- public static class A2 implements Serializable, Cloneable {};
- public static class A3 implements Serializable, Cloneable {};
-
+ public static class B1 extends A implements Serializable {
+ private static final long serialVersionUID = 1L;};
+ public static class B2 extends A implements Serializable {
+ private static final long serialVersionUID = 1L;};
+ public static class A2 implements Serializable, Cloneable {
+ private static final long serialVersionUID = 1L;};
+ public static class A3 implements Serializable, Cloneable {
+ private static final long serialVersionUID = 1L;};
public static class B3 extends A {};
- public static class C31 extends B3 implements Serializable {};
+ public static class C31 extends B3 implements Serializable {
+ private static final long serialVersionUID = 1L;};
public void dummy(Serializable s) {}
@@ -359,6 +363,7 @@ public class AnalyzerTest extends TestCase {
}
+ @SuppressWarnings("rawtypes")
public void reusedLocalMerge() {
ArrayList list = new ArrayList();
try {
diff --git a/src/test/test/javassist/bytecode/analysis/DomTreePrinter.java b/src/test/test/javassist/bytecode/analysis/DomTreePrinter.java
new file mode 100644
index 0000000..2ada4ac
--- /dev/null
+++ b/src/test/test/javassist/bytecode/analysis/DomTreePrinter.java
@@ -0,0 +1,77 @@
+package test.javassist.bytecode.analysis;
+
+import javassist.ClassPool;
+import javassist.bytecode.analysis.ControlFlow;
+import javassist.bytecode.analysis.ControlFlow.Block;
+import javassist.bytecode.analysis.ControlFlow.Node;
+
+public class DomTreePrinter {
+ public static void main(String[] args) throws Exception {
+ ClassPool pool = ClassPool.getDefault();
+ ControlFlow cf = new ControlFlow(pool.get(args[0]).getDeclaredMethod(args[1]));
+ Block[] blocks = cf.basicBlocks();
+ for (int i = 0; i < blocks.length; i++)
+ System.out.println(i + ": " + blocks[i]);
+
+ Node[] dom = cf.dominatorTree();
+ for (int i = 0; i < dom.length; i++)
+ System.out.println(i + ": " + dom[i]);
+
+ Node[] pdom = cf.postDominatorTree();
+ for (int i = 0; i < pdom.length; i++)
+ System.out.println(i + ": " + pdom[i]);
+ }
+
+ public int dummy(int n, int[] array) {
+ for (int i = 0; i < n; i++) {
+ if (array[i] > 0)
+ break;
+ if (array[i] > -1)
+ continue;
+ array[0]++;
+ array[1]++;
+ }
+ return array[0];
+ }
+
+ public int dummy2(int n, int[] array) {
+ int i = 0;
+ while (i < n) {
+ if (array[i] > 0)
+ break;
+ if (array[i++] > -1)
+ continue;
+ array[0]++;
+ array[1]++;
+ }
+ return array[0];
+ }
+
+ public int dummy3(int n, int[] array) {
+ int i = 0;
+ do {
+ if (array[i] > 0)
+ break;
+ if (array[i++] > -1)
+ continue;
+ array[0]++;
+ array[1]++;
+ } while (i < n);
+ return array[0];
+ }
+
+ public int dummy4(int n, int[] array) {
+ int i = 0;
+ do {
+ if (array[i] > 0)
+ if (array[i++] > -1)
+ continue;
+ else
+ return 0;
+ array[0]++;
+ array[1]++;
+ } while (i < n);
+ return array[0];
+ }
+
+}
diff --git a/src/test/test/javassist/bytecode/analysis/DomTreeTest.java b/src/test/test/javassist/bytecode/analysis/DomTreeTest.java
new file mode 100644
index 0000000..7ea54b8
--- /dev/null
+++ b/src/test/test/javassist/bytecode/analysis/DomTreeTest.java
@@ -0,0 +1,113 @@
+package test.javassist.bytecode.analysis;
+
+import javassist.ClassPool;
+import javassist.bytecode.analysis.ControlFlow;
+import javassist.bytecode.analysis.ControlFlow.Block;
+import javassist.bytecode.analysis.ControlFlow.Node;
+import junit.framework.TestCase;
+
+public class DomTreeTest extends TestCase {
+ private ClassPool pool = ClassPool.getDefault();
+
+ public void testDomtree() throws Exception {
+ ControlFlow cf = new ControlFlow(pool.get(DomTreeTest.class.getName()).getDeclaredMethod("test1"));
+ Block[] blocks = cf.basicBlocks();
+ // for (int i = 0; i < blocks.length; i++)
+ // System.out.println(i + ": " + blocks[i]);
+ testBlock(blocks[0], new int[] {}, new int[] { 11, 6 } );
+ testBlock(blocks[1], new int[] { 0 }, new int[] { 17, 11 } );
+ testBlock(blocks[2], new int[] { 0, 6 }, new int[] { 19, 17 });
+ testBlock(blocks[3], new int[] { 6, 11 }, new int[] { 19 });
+ testBlock(blocks[4], new int[] { 11, 17 }, new int[] {});
+
+ Node[] dom = cf.dominatorTree();
+ assertNull(dom[0].parent());
+ assertEquals(0, dom[1].parent().block().position());
+ assertEquals(0, dom[2].parent().block().position());
+ assertEquals(0, dom[3].parent().block().position());
+ assertEquals(0, dom[4].parent().block().position());
+
+ Node[] pdom = cf.postDominatorTree();
+ assertEquals(19, pdom[0].parent().block().position());
+ assertEquals(19, pdom[1].parent().block().position());
+ assertEquals(19, pdom[2].parent().block().position());
+ assertEquals(19, pdom[3].parent().block().position());
+ assertNull(pdom[4].parent());
+ }
+
+ private void testBlock(Block b, int[] incoming, int[] outgoing) {
+ assertEquals(incoming.length, b.incomings());
+ int i = 0;
+ for (int index: incoming)
+ assertEquals(index, b.incoming(i++).position());
+ i = 0;
+ assertEquals(outgoing.length, b.exits());
+ for (int index: outgoing)
+ assertEquals(index, b.exit(i++).position());
+ }
+
+ private void testNode(Node n, int[] incoming, int[] outgoing) {
+ int i = 0;
+ for (int index: incoming)
+ assertEquals(index, n.parent().block().index());
+ }
+
+ public void test1(){
+ int k=0;
+ if (k != 0 && k!=2 || k < 7) {
+ k = 3 ;
+ }
+ }
+
+ public void testDomtree2() throws Exception {
+ ControlFlow cf = new ControlFlow(pool.get(DomTreeTest.class.getName()).getDeclaredMethod("test2"));
+ Block[] blocks = cf.basicBlocks();
+ // for (int i = 0; i < blocks.length; i++)
+ // System.out.println(i + ": " + blocks[i]);
+ testBlock(blocks[0], new int[] { 7 }, new int[] { 14, 7 } );
+ testBlock(blocks[1], new int[] { 0 }, new int[] { 0, 12 } );
+ testBlock(blocks[2], new int[] { 7 }, new int[] {});
+ testBlock(blocks[3], new int[] { 0 }, new int[] {});
+
+ Node[] dom = cf.dominatorTree();
+ assertNull(dom[0].parent());
+ assertEquals(0, dom[1].parent().block().position());
+ assertEquals(7, dom[2].parent().block().position());
+ assertEquals(0, dom[3].parent().block().position());
+
+ Node[] pdom = cf.postDominatorTree();
+ assertNull(pdom[0].parent());
+ assertNull(pdom[1].parent());
+ assertNull(pdom[2].parent());
+ assertNull(pdom[3].parent());
+ }
+
+ public int test2(int i){
+ while (i-- > 0)
+ if (i == 3)
+ return 1;
+
+ return i + 3;
+ }
+
+ public void testDomtree3() throws Exception {
+ ControlFlow cf = new ControlFlow(pool.get(DomTreeTest.class.getName()).getDeclaredMethod("test3"));
+ Block[] blocks = cf.basicBlocks();
+ for (int i = 0; i < blocks.length; i++)
+ System.out.println(blocks[i]);
+ }
+
+ public int test3(int i, int j) {
+ while (i > 0) {
+ try {
+ j++;
+ }
+ catch (Throwable t) {
+ j = 0;
+ }
+ i--;
+ }
+
+ return j;
+ }
+}
diff --git a/src/test/test/javassist/bytecode/analysis/ScannerTest.java b/src/test/test/javassist/bytecode/analysis/ScannerTest.java
index 10f2936..56114ba 100644
--- a/src/test/test/javassist/bytecode/analysis/ScannerTest.java
+++ b/src/test/test/javassist/bytecode/analysis/ScannerTest.java
@@ -72,7 +72,7 @@ public class ScannerTest extends TestCase {
assertNotNull(sub);
assertEquals(sub.start(), start);
for (int i = 0; i < callers.length; i++)
- assertTrue(sub.callers().contains(new Integer(callers[i])));
+ assertTrue(sub.callers().contains(Integer.valueOf(callers[i])));
}
private static void generate(ClassPool pool) throws CannotCompileException, IOException, NotFoundException {
diff --git a/src/test/test/javassist/convert/ArrayAccessReplaceTest.java b/src/test/test/javassist/convert/ArrayAccessReplaceTest.java
index 09387ce..cebd185 100644
--- a/src/test/test/javassist/convert/ArrayAccessReplaceTest.java
+++ b/src/test/test/javassist/convert/ArrayAccessReplaceTest.java
@@ -1,7 +1,5 @@
package test.javassist.convert;
-import java.net.URL;
-import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;
@@ -11,7 +9,7 @@ import javassist.CtClass;
import junit.framework.TestCase;
public class ArrayAccessReplaceTest extends TestCase {
- private static SimpleInterface simple;
+ private static SimpleInterface simple = null;
public void setUp() throws Exception {
ClassPool pool = new ClassPool(true);
@@ -21,7 +19,9 @@ public class ArrayAccessReplaceTest extends TestCase {
converter.replaceArrayAccess(echoClass, new CodeConverter.DefaultArrayAccessReplacementMethodNames());
simpleClass.instrument(converter);
//simpleClass.writeFile("/tmp");
- simple = (SimpleInterface) simpleClass.toClass(new URLClassLoader(new URL[0], getClass().getClassLoader()), Class.class.getProtectionDomain()).newInstance();
+
+ simple = (SimpleInterface)new javassist.Loader.Simple().invokeDefineClass(simpleClass)
+ .getConstructor().newInstance();
}
public void testComplex() throws Exception {
@@ -31,8 +31,10 @@ public class ArrayAccessReplaceTest extends TestCase {
CodeConverter converter = new CodeConverter();
converter.replaceArrayAccess(clazz, new CodeConverter.DefaultArrayAccessReplacementMethodNames());
clazz.instrument(converter);
- ComplexInterface instance = (ComplexInterface) clazz.toClass(new URLClassLoader(new URL[0], getClass().getClassLoader()), Class.class.getProtectionDomain()).newInstance();
- assertEquals(new Integer(5), instance.complexRead(4));
+ ComplexInterface instance
+ = (ComplexInterface)new javassist.Loader.Simple().invokeDefineClass(clazz)
+ .getConstructor().newInstance();
+ assertEquals(Integer.valueOf(5), instance.complexRead(4));
}
public void testBoolean() throws Exception {
@@ -119,11 +121,11 @@ public class ArrayAccessReplaceTest extends TestCase {
public void testObject() throws Exception {
for (int i = 0; i < 100; i++) {
- simple.setObject(i, new Integer(i));
+ simple.setObject(i, Integer.valueOf(i));
}
for (int i = 0; i < 100; i++) {
- assertEquals(new Integer(i), simple.getObject(i));
+ assertEquals(Integer.valueOf(i), simple.getObject(i));
}
}
@@ -147,6 +149,7 @@ public class ArrayAccessReplaceTest extends TestCase {
}
}
+ @SuppressWarnings({"rawtypes","unchecked"})
public static class Echo {
public static Map byteMap = new HashMap();
public static Map charMap = new HashMap();
@@ -158,67 +161,67 @@ public class ArrayAccessReplaceTest extends TestCase {
public static Map shortMap = new HashMap();
public static Object arrayReadObject(Object array, int index) {
- return objectMap.get(new Integer(index));
+ return objectMap.get(Integer.valueOf(index));
}
public static void arrayWriteObject(Object array, int index, Object element) {
- objectMap.put(new Integer(index), element);
+ objectMap.put(Integer.valueOf(index), element);
}
public static byte arrayReadByteOrBoolean(Object array, int index) {
- return ((Byte)byteMap.get(new Integer(index))).byteValue();
+ return ((Byte)byteMap.get(Integer.valueOf(index))).byteValue();
}
public static void arrayWriteByteOrBoolean(Object array, int index, byte element) {
- byteMap.put(new Integer(index), new Byte(element));
+ byteMap.put(Integer.valueOf(index), Byte.valueOf(element));
}
public static char arrayReadChar(Object array, int index) {
- return ((Character)charMap.get(new Integer(index))).charValue();
+ return ((Character)charMap.get(Integer.valueOf(index))).charValue();
}
public static void arrayWriteChar(Object array, int index, char element) {
- charMap.put(new Integer(index), new Character(element));
+ charMap.put(Integer.valueOf(index), Character.valueOf(element));
}
public static double arrayReadDouble(Object array, int index) {
- return ((Double)doubleMap.get(new Integer(index))).doubleValue();
+ return ((Double)doubleMap.get(Integer.valueOf(index))).doubleValue();
}
public static void arrayWriteDouble(Object array, int index, double element) {
- doubleMap.put(new Integer(index), new Double(element));
+ doubleMap.put(Integer.valueOf(index), Double.valueOf(element));
}
public static float arrayReadFloat(Object array, int index) {
- return ((Float)floatMap.get(new Integer(index))).floatValue();
+ return ((Float)floatMap.get(Integer.valueOf(index))).floatValue();
}
public static void arrayWriteFloat(Object array, int index, float element) {
- floatMap.put(new Integer(index), new Float(element));
+ floatMap.put(Integer.valueOf(index), Float.valueOf(element));
}
public static int arrayReadInt(Object array, int index) {
- return ((Integer)intMap.get(new Integer(index))).intValue();
+ return ((Integer)intMap.get(Integer.valueOf(index))).intValue();
}
public static void arrayWriteInt(Object array, int index, int element) {
- intMap.put(new Integer(index), new Integer(element));
+ intMap.put(Integer.valueOf(index), Integer.valueOf(element));
}
public static long arrayReadLong(Object array, int index) {
- return ((Long)longMap.get(new Integer(index))).longValue();
+ return ((Long)longMap.get(Integer.valueOf(index))).longValue();
}
public static void arrayWriteLong(Object array, int index, long element) {
- longMap.put(new Integer(index), new Long(element));
+ longMap.put(Integer.valueOf(index), Long.valueOf(element));
}
public static short arrayReadShort(Object array, int index) {
- return ((Short)shortMap.get(new Integer(index))).shortValue();
+ return ((Short)shortMap.get(Integer.valueOf(index))).shortValue();
}
public static void arrayWriteShort(Object array, int index, short element) {
- shortMap.put(new Integer(index), new Short(element));
+ shortMap.put(Integer.valueOf(index), Short.valueOf(element));
}
}
@@ -393,7 +396,7 @@ public class ArrayAccessReplaceTest extends TestCase {
private static Integer justRead;
public static Object arrayReadObject(Object array, int offset) {
- return new Integer(justRead.intValue() + offset);
+ return Integer.valueOf(justRead.intValue() + offset);
}
public static void arrayWriteObject(Object array, int offset, Object element) {
@@ -401,7 +404,7 @@ public class ArrayAccessReplaceTest extends TestCase {
}
public Object getInteger(int i) {
- return (Object) new Integer(i);
+ return (Object) Integer.valueOf(i);
}
public Number complexRead(int x) {
diff --git a/src/test/test/javassist/proxy/Foo.java b/src/test/test/javassist/proxy/Foo.java
new file mode 100644
index 0000000..fd9e6e3
--- /dev/null
+++ b/src/test/test/javassist/proxy/Foo.java
@@ -0,0 +1,16 @@
+package test.javassist.proxy;
+
+public class Foo {
+ public String doSomething() {
+ return "I'm doing something";
+ }
+
+ public Object getHandler() {
+ return "This is a secret handler";
+ }
+}
+
+class Foo2 {
+ public String doSomething() { return "do something"; }
+ public String getHandler() { return "return a string"; }
+} \ No newline at end of file
diff --git a/src/test/test/javassist/proxy/JBPAPP9257Test.java b/src/test/test/javassist/proxy/JBPAPP9257Test.java
new file mode 100644
index 0000000..773e18b
--- /dev/null
+++ b/src/test/test/javassist/proxy/JBPAPP9257Test.java
@@ -0,0 +1,68 @@
+package test.javassist.proxy;
+
+import java.lang.reflect.Method;
+import javassist.util.proxy.ProxyFactory;
+import javassist.util.proxy.MethodHandler;
+import javassist.util.proxy.MethodFilter;
+import javassist.util.proxy.ProxyObject;
+import javassist.util.proxy.Proxy;
+import junit.framework.TestCase;
+
+@SuppressWarnings({"rawtypes","unchecked"})
+public class JBPAPP9257Test extends TestCase {
+ public void testGetHandler() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.setSuperclass(Foo.class);
+ f.setFilter(new MethodFilter() {
+ public boolean isHandled(Method m) {
+ // ignore finalize()
+ return !m.getName().equals("finalize");
+ }
+ });
+ Class c = f.createClass();
+ MethodHandler mi = new MethodHandler() {
+ public Object invoke(Object self, Method m, Method proceed,
+ Object[] args) throws Throwable {
+ System.out.println("Name: " + m.getName());
+ return proceed.invoke(self, args) + "!"; // execute the original
+ // method.
+ }
+ };
+ Foo foo = (Foo)c.getConstructor().newInstance();
+ try {
+ ((ProxyObject)foo).setHandler(mi);
+ fail("foo is a ProxyObject!");
+ } catch (ClassCastException e) {}
+ ((Proxy)foo).setHandler(mi);
+ assertEquals("I'm doing something!", foo.doSomething());
+ assertEquals("This is a secret handler!", foo.getHandler());
+ }
+
+ public void testGetHandler2() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.setSuperclass(Foo2.class);
+ f.setFilter(new MethodFilter() {
+ public boolean isHandled(Method m) {
+ // ignore finalize()
+ return !m.getName().equals("finalize");
+ }
+ });
+ Class c = f.createClass();
+ MethodHandler mi = new MethodHandler() {
+ public Object invoke(Object self, Method m, Method proceed,
+ Object[] args) throws Throwable {
+ System.out.println("Name: " + m.getName());
+ return proceed.invoke(self, args) + "!"; // execute the original
+ // method.
+ }
+ };
+ Foo2 foo = (Foo2)c.getConstructor().newInstance();
+ try {
+ ((ProxyObject)foo).setHandler(mi);
+ fail("foo is a ProxyObject!");
+ } catch (ClassCastException e) {}
+ ((Proxy)foo).setHandler(mi);
+ assertEquals("do something!", foo.doSomething());
+ assertEquals("return a string!", foo.getHandler());
+ }
+}
diff --git a/src/test/test/javassist/proxy/ProxyCacheGCTest.java b/src/test/test/javassist/proxy/ProxyCacheGCTest.java
index 379fefc..e47c438 100644
--- a/src/test/test/javassist/proxy/ProxyCacheGCTest.java
+++ b/src/test/test/javassist/proxy/ProxyCacheGCTest.java
@@ -11,6 +11,7 @@ import junit.framework.TestCase;
* test which checks that proxy classes are not retained after their classloader is released.
* this is a before and after test which validates JASSIST-104
*/
+@SuppressWarnings({"rawtypes","unchecked"})
public class ProxyCacheGCTest extends TestCase
{
/**
@@ -89,12 +90,12 @@ public class ProxyCacheGCTest extends TestCase
// now create a proxyfactory and use it to create a proxy
ProxyFactory factory = new ProxyFactory();
- Class javaTargetClass = classPool.toClass(ctTargetClass);
- Class javaHandlerClass = classPool.toClass(ctHandlerClass);
- Class javaFilterClass = classPool.toClass(ctFilterClass);
+ Class javaTargetClass = classPool.toClass(ctTargetClass, test.javassist.DefineClassCapability.class);
+ Class javaHandlerClass = classPool.toClass(ctHandlerClass, test.javassist.DefineClassCapability.class);
+ Class javaFilterClass = classPool.toClass(ctFilterClass, test.javassist.DefineClassCapability.class);
- MethodHandler handler= (MethodHandler)javaHandlerClass.newInstance();
- MethodFilter filter = (MethodFilter)javaFilterClass.newInstance();
+ MethodHandler handler= (MethodHandler)javaHandlerClass.getConstructor().newInstance();
+ MethodFilter filter = (MethodFilter)javaFilterClass.getConstructor().newInstance();
// ok, now create a factory and a proxy class and proxy from that factory
factory.setFilter(filter);
@@ -102,7 +103,7 @@ public class ProxyCacheGCTest extends TestCase
// factory.setSuperclass(Object.class);
Class proxyClass = factory.createClass();
- Object target = proxyClass.newInstance();
+ Object target = proxyClass.getConstructor().newInstance();
((ProxyObject)target).setHandler(handler);
} catch (Exception e) {
e.printStackTrace();
diff --git a/src/test/test/javassist/proxy/ProxyFactoryCompatibilityTest.java b/src/test/test/javassist/proxy/ProxyFactoryCompatibilityTest.java
index 5b72d82..861abb7 100644
--- a/src/test/test/javassist/proxy/ProxyFactoryCompatibilityTest.java
+++ b/src/test/test/javassist/proxy/ProxyFactoryCompatibilityTest.java
@@ -3,6 +3,7 @@ package test.javassist.proxy;
import javassist.*;
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
+import javassist.util.proxy.Proxy;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyObject;
import junit.framework.TestCase;
@@ -15,6 +16,7 @@ import java.lang.reflect.Method;
* automatically disabled if this legacy api is used. it also exercises the new style
* api, ensuring that caching works correctly with this model.
*/
+@SuppressWarnings({"rawtypes","unchecked","unused"})
public class ProxyFactoryCompatibilityTest extends TestCase
{
private ClassPool basePool;
@@ -51,14 +53,14 @@ public class ProxyFactoryCompatibilityTest extends TestCase
// create the same class twice and check that it is reused
Class proxyClass1 = factory.createClass();
System.out.println("created first class " + proxyClass1.getName());
- TestClass proxy1 = (TestClass)proxyClass1.newInstance();
+ TestClass proxy1 = (TestClass)proxyClass1.getConstructor().newInstance();
((ProxyObject) proxy1).setHandler(handler);
proxy1.testMethod();
assertTrue(proxy1.isTestCalled());
Class proxyClass2 = factory.createClass();
System.out.println("created second class " + proxyClass2.getName());
- TestClass proxy2 = (TestClass)proxyClass2.newInstance();
+ TestClass proxy2 = (TestClass)proxyClass2.getConstructor().newInstance();
((ProxyObject) proxy2).setHandler(handler);
proxy2.testMethod();
assertTrue(proxy2.isTestCalled());
@@ -71,22 +73,23 @@ public class ProxyFactoryCompatibilityTest extends TestCase
factory.setSuperclass(TestClass.class);
factory.setInterfaces(new Class[] { TestInterface.class});
factory.setFilter(filter);
- factory.setHandler(handler);
// create the same class twice and check that it is reused
Class proxyClass3 = factory.createClass();
System.out.println("created third class " + proxyClass3.getName());
- TestClass proxy3 = (TestClass)proxyClass3.newInstance();
+ TestClass proxy3 = (TestClass)proxyClass3.getConstructor().newInstance();
+ ((Proxy)proxy3).setHandler(handler);
proxy3.testMethod();
assertTrue(proxy3.isTestCalled());
Class proxyClass4 = factory.createClass();
System.out.println("created fourth class " + proxyClass4.getName());
- TestClass proxy4 = (TestClass)proxyClass4.newInstance();
+ TestClass proxy4 = (TestClass)proxyClass4.getConstructor().newInstance();
+ ((Proxy)proxy4).setHandler(handler);
proxy4.testMethod();
assertTrue(proxy4.isTestCalled());
- assertTrue(proxyClass3 != proxyClass4);
+ assertTrue(proxyClass3 == proxyClass4);
}
/**
diff --git a/src/test/test/javassist/proxy/ProxySerializationTest.java b/src/test/test/javassist/proxy/ProxySerializationTest.java
index 28125de..39d94f7 100644
--- a/src/test/test/javassist/proxy/ProxySerializationTest.java
+++ b/src/test/test/javassist/proxy/ProxySerializationTest.java
@@ -13,6 +13,7 @@ import java.lang.reflect.Method;
* {@link javassist.util.proxy.ProxyObjectOutputStream} and @link javassist.util.proxy.ProxyObjectInputStream}
* reuses classes located in the proxy cache. This tests the fixes provided for JASSIST-42 and JASSIST-97.
*/
+@SuppressWarnings({"rawtypes","unchecked","unused","resource"})
public class ProxySerializationTest extends TestCase
{
public void testSerialization()
@@ -80,6 +81,9 @@ public class ProxySerializationTest extends TestCase
public static class TestFilter implements MethodFilter, Serializable
{
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public boolean isHandled(Method m) {
if (m.getName().equals("getName")) {
return true;
@@ -105,6 +109,9 @@ public class ProxySerializationTest extends TestCase
public static class TestHandler implements MethodHandler, Serializable
{
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable
{
return proceed.invoke(self, args);
@@ -127,6 +134,8 @@ public class ProxySerializationTest extends TestCase
public static class TestClass implements Serializable
{
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
public String name;
public TestClass()
diff --git a/src/test/test/javassist/proxy/ProxySimpleTest.java b/src/test/test/javassist/proxy/ProxySimpleTest.java
index f74fce4..6329850 100644
--- a/src/test/test/javassist/proxy/ProxySimpleTest.java
+++ b/src/test/test/javassist/proxy/ProxySimpleTest.java
@@ -1,7 +1,5 @@
package test.javassist.proxy;
-import junit.framework.TestCase;
-
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
@@ -9,25 +7,74 @@ import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
+import javassist.util.proxy.MethodFilter;
+import javassist.util.proxy.MethodHandler;
+import javassist.util.proxy.Proxy;
import javassist.util.proxy.ProxyFactory;
+import junit.framework.TestCase;
+@SuppressWarnings({"rawtypes","unchecked"})
public class ProxySimpleTest extends TestCase {
+
+ String testResult;
+
+ public void testProxyFactory() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.writeDirectory = "./proxy";
+ f.setSuperclass(Foo.class);
+ f.setFilter(new MethodFilter() {
+ public boolean isHandled(Method m) {
+ return m.getName().startsWith("f");
+ }
+ });
+ Class c = f.createClass();
+ MethodHandler mi = new MethodHandler() {
+ public Object invoke(Object self, Method m, Method proceed,
+ Object[] args) throws Throwable {
+ testResult += args[0].toString();
+ return proceed.invoke(self, args); // execute the original method.
+ }
+ };
+ Foo foo = (Foo)c.getConstructor().newInstance();
+ ((Proxy)foo).setHandler(mi);
+ testResult = "";
+ foo.foo(1);
+ foo.foo2(2);
+ foo.bar(3);
+ assertEquals("12", testResult);
+ }
+
+ public static class Foo {
+ public int foo(int i) { return i + 1; }
+ public int foo2(int i) { return i + 2; }
+ public int bar(int i) { return i + 1; }
+ }
+
public void testReadWrite() throws Exception {
final String fileName = "read-write.bin";
ProxyFactory.ClassLoaderProvider cp = ProxyFactory.classLoaderProvider;
- ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
- public ClassLoader get(ProxyFactory pf) {
- return new javassist.Loader();
- }
- };
- ProxyFactory pf = new ProxyFactory();
- pf.setSuperclass(ReadWriteData.class);
- Object data = pf.createClass().newInstance();
- // Object data = new ReadWriteData();
- ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName));
- oos.writeObject(data);
- oos.close();
- ProxyFactory.classLoaderProvider = cp;
+ try {
+ ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
+ public ClassLoader get(ProxyFactory pf) {
+ /* If javassist.Loader is returned, the super type of ReadWriteData class,
+ * which is Serializable, is loaded by javassist.Loader as well as ReadWriteData.
+ * This breaks the implementation of the object serializer.
+ */
+ // return new javassist.Loader();
+ return Thread.currentThread().getContextClassLoader();
+ }
+ };
+ ProxyFactory pf = new ProxyFactory();
+ pf.setSuperclass(ReadWriteData.class);
+ Object data = pf.createClass().getConstructor().newInstance();
+ // Object data = new ReadWriteData();
+ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName));
+ oos.writeObject(data);
+ oos.close();
+ }
+ finally {
+ ProxyFactory.classLoaderProvider = cp;
+ }
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
Object data2 = ois.readObject();
@@ -37,28 +84,282 @@ public class ProxySimpleTest extends TestCase {
}
public static class ReadWriteData implements Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public int foo() { return 4; }
}
public void testWriteReplace() throws Exception {
ProxyFactory pf = new ProxyFactory();
pf.setSuperclass(WriteReplace.class);
- Object data = pf.createClass().newInstance();
+ Object data = pf.createClass().getConstructor().newInstance();
assertEquals(data, ((WriteReplace)data).writeReplace());
ProxyFactory pf2 = new ProxyFactory();
pf2.setSuperclass(WriteReplace2.class);
- Object data2 = pf2.createClass().newInstance();
+ Object data2 = pf2.createClass().getConstructor().newInstance();
Method meth = data2.getClass().getDeclaredMethod("writeReplace", new Class[0]);
assertEquals("javassist.util.proxy.SerializedProxy",
meth.invoke(data2, new Object[0]).getClass().getName());
}
public static class WriteReplace implements Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
public Object writeReplace() { return this; }
}
public static class WriteReplace2 implements Serializable {
- public Object writeReplace(int i) { return new Integer(i); }
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ public Object writeReplace(int i) { return Integer.valueOf(i); }
+ }
+
+ String value244;
+
+ public void testJIRA244() throws Exception {
+ ProxyFactory factory = new ProxyFactory();
+ factory.setSuperclass(Extended244.class);
+ Extended244 e = (Extended244)factory.create(null, null, new MethodHandler() {
+ @Override
+ public Object invoke(Object self, Method thisMethod,
+ Method proceed, Object[] args) throws Throwable {
+ value244 += thisMethod.getDeclaringClass().getName();
+ return proceed.invoke(self);
+ }
+ });
+
+ value244 = "";
+ assertEquals("base", e.base());
+ System.out.println(value244);
+ assertEquals(Extended244.class.getName(), value244);
+
+ value244 = "";
+ assertEquals("ext", e.extended());
+ System.out.println(value244);
+ assertEquals(Extended244.class.getName(), value244);
+
+ value244 = "";
+ assertEquals("base2ext2", e.base2());
+ System.out.println(value244);
+ assertEquals(Extended244.class.getName(), value244);
+ }
+
+ // if Base244 is private, then Extended244 has a bridge method for base().
+ private static abstract class Base244 {
+ public String base() { return "base"; }
+ public String base2() { return "base2"; }
+ }
+
+ public static class Extended244 extends Base244 {
+ public String extended() { return "ext"; }
+ public String base2() { return super.base2() + "ext2"; }
+ }
+
+ String valueDefaultMethods = "";
+
+ public void testDefaultMethods() throws Exception {
+ valueDefaultMethods = "";
+ ProxyFactory f = new ProxyFactory();
+ f.writeDirectory = "./proxy";
+ f.setSuperclass(Default3.class);
+ Class c = f.createClass();
+ MethodHandler mi = new MethodHandler() {
+ public Object invoke(Object self, Method m, Method proceed,
+ Object[] args) throws Throwable {
+ valueDefaultMethods += "1";
+ return proceed.invoke(self, args); // execute the original method.
+ }
+ };
+ Default3 foo = (Default3)c.getConstructor().newInstance();
+ ((Proxy)foo).setHandler(mi);
+ foo.foo();
+ foo.bar();
+ assertEquals("11", valueDefaultMethods);
+ }
+
+ public void testDefaultMethods2() throws Exception {
+ valueDefaultMethods = "";
+ ProxyFactory f = new ProxyFactory();
+ f.writeDirectory = "./proxy";
+ f.setInterfaces(new Class[] { Default2.class });
+ Class c = f.createClass();
+ MethodHandler mi = new MethodHandler() {
+ public Object invoke(Object self, Method m, Method proceed,
+ Object[] args) throws Throwable {
+ valueDefaultMethods += "1";
+ return proceed.invoke(self, args); // execute the original method.
+ }
+ };
+ Default2 foo = (Default2)c.getConstructor().newInstance();
+ ((Proxy)foo).setHandler(mi);
+ foo.foo();
+ foo.bar();
+ assertEquals("11", valueDefaultMethods);
+ }
+
+ public static interface Default1 {
+ default int foo() { return 0; }
+ default int baz() { return 2; }
+ }
+
+ public static interface Default2 extends Default1 {
+ default int bar() { return 1; }
+ }
+
+ public static class Default3 implements Default2 {
+ public int foo() { return Default2.super.foo(); }
+ }
+
+ public static class Default4 extends Default3 {
+ public int baz() { return super.baz(); }
+ }
+
+ public void testPublicProxy() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.writeDirectory = "./proxy";
+ f.setSuperclass(PubProxy.class);
+ Class c = f.createClass();
+ MethodHandler mi = new MethodHandler() {
+ public Object invoke(Object self, Method m, Method proceed,
+ Object[] args) throws Throwable {
+ PubProxy.result += args[0].toString();
+ return proceed.invoke(self, args);
+ }
+ };
+ PubProxy.result = "";
+ PubProxy foo = (PubProxy)c.getConstructor().newInstance();
+ ((Proxy)foo).setHandler(mi);
+ foo.foo(1);
+ foo.bar(2);
+ foo.baz(3);
+ assertEquals("c1p2q3r", PubProxy.result);
+ }
+
+ public static class PubProxy {
+ public static String result;
+ public PubProxy() { result += "c"; }
+ PubProxy(int i) { result += "d"; }
+ void foo(int i) { result += "p"; }
+ protected void bar(int i) { result += "q"; }
+ public void baz(int i) { result += "r"; }
+ }
+
+ public void testPublicProxy2() throws Exception {
+ boolean b = ProxyFactory.onlyPublicMethods;
+ ProxyFactory.onlyPublicMethods = true;
+ ProxyFactory f = new ProxyFactory();
+ f.writeDirectory = "./proxy";
+ f.setSuperclass(PubProxy2.class);
+ Class c = f.createClass();
+ MethodHandler mi = new MethodHandler() {
+ public Object invoke(Object self, Method m, Method proceed,
+ Object[] args) throws Throwable {
+ PubProxy2.result += args[0].toString();
+ return proceed.invoke(self, args);
+ }
+ };
+
+ PubProxy2.result = "";
+ try {
+ PubProxy2 foo = (PubProxy2)c.getConstructor().newInstance();
+ ((Proxy)foo).setHandler(mi);
+ foo.foo(1); // mi does not intercept this call.
+ foo.bar(2);
+ foo.baz(3);
+ assertEquals("cp2q3r", PubProxy2.result);
+ }
+ finally {
+ ProxyFactory.onlyPublicMethods = b;
+ }
+ }
+
+ public static class PubProxy2 {
+ public static String result;
+ public PubProxy2() { result += "c"; }
+ PubProxy2(int i) { result += "d"; }
+ void foo(int i) { result += "p"; }
+ protected void bar(int i) { result += "q"; }
+ public void baz(int i) { result += "r"; }
+ }
+
+
+ String value267;
+
+ public void testJIRA267() throws Exception {
+ Extended267 extended267 = new Extended267();
+ for (Method method: extended267.getClass().getMethods()) {
+ System.out.println(method.getName() + "::" + method.getModifiers() + ":" + method.getParameterCount() + ":" + method.isSynthetic() + ":" + method.isBridge());
+ }
+ ProxyFactory factory = new ProxyFactory();
+ factory.setSuperclass(Extended267.class);
+ Extended267 e = (Extended267)factory.create(null, null, new MethodHandler() {
+ @Override
+ public Object invoke(Object self, Method thisMethod,
+ Method proceed, Object[] args) throws Throwable {
+ value267 += thisMethod.getDeclaringClass().getName();
+ return proceed.invoke(self, args);
+ }
+ });
+
+ value267 = "";
+ assertEquals("base", e.base());
+ System.out.println(value267);
+ assertEquals(Extended267.class.getName(), value267);
+
+ value267 = "";
+ assertEquals("base2", e.base("2"));
+ System.out.println(value267);
+ assertEquals(Extended267.class.getName(), value267);
+
+ value267 = "";
+ assertEquals("extended22", e.base(2));
+ System.out.println(value267);
+ assertEquals(Extended267.class.getName(), value267);
+ }
+
+ private static abstract class Base267 {
+ public String base() { return "base"; }
+ public String base(String s) { return "base" + s; }
+ public String base(Integer i) { return "base" + i; }
+ }
+
+ public static class Extended267 extends Base267 {
+ public String base(Integer i) { return "extended" + i + i; }
+ }
+
+ String value267b;
+
+ public void testJIRA267b() throws Exception {
+ Extended267b extended267 = new Extended267b();
+ ProxyFactory factory = new ProxyFactory();
+ factory.setSuperclass(Extended267b.class);
+ Extended267b e = (Extended267b)factory.create(null, null, new MethodHandler() {
+ @Override
+ public Object invoke(Object self, Method thisMethod,
+ Method proceed, Object[] args) throws Throwable {
+ value267b += thisMethod.getDeclaringClass().getName();
+ value267b += ";" + thisMethod.getReturnType().getName();
+ return proceed.invoke(self, args);
+ }
+ });
+
+ value267b = "";
+ assertEquals("extended", e.base());
+ System.out.println(value267b);
+ assertEquals(Extended267b.class.getName() + ";" + String.class.getName(), value267b);
+ }
+
+ public static class Base267b {
+ public Object base() { return "base"; }
+ }
+
+ // Extended267b has a bridge method for base():Object,
+ // since Extended267b#base() is covariant.
+ public static class Extended267b extends Base267b {
+ public String base() { return "extended"; }
}
}
diff --git a/src/test/test/javassist/tools/DefineClassCapability.java b/src/test/test/javassist/tools/DefineClassCapability.java
new file mode 100644
index 0000000..fc3dc4e
--- /dev/null
+++ b/src/test/test/javassist/tools/DefineClassCapability.java
@@ -0,0 +1,7 @@
+/*
+ * This is used as a capability for running CtClass#toClass().
+ */
+package test.javassist.tools;
+
+public class DefineClassCapability {
+}
diff --git a/src/test/test/javassist/tools/DummyClass.java b/src/test/test/javassist/tools/DummyClass.java
new file mode 100644
index 0000000..ab9400f
--- /dev/null
+++ b/src/test/test/javassist/tools/DummyClass.java
@@ -0,0 +1,9 @@
+package test.javassist.tools;
+
+public class DummyClass {
+
+ @SuppressWarnings("unused")
+ private String dummyString = "dummyStringValue";
+
+ public void dummyMethod(){}
+}
diff --git a/src/test/test1/AddClassInfo.java b/src/test/test1/AddClassInfo.java
new file mode 100644
index 0000000..da1b0cf
--- /dev/null
+++ b/src/test/test1/AddClassInfo.java
@@ -0,0 +1,7 @@
+package test1;
+
+public class AddClassInfo {
+ public int value;
+ public int foo() { return 1; }
+ public AddClassInfo() { System.out.println("ok"); }
+}
diff --git a/src/test/test1/ArrayAccess.java b/src/test/test1/ArrayAccess.java
new file mode 100644
index 0000000..98cbf4b
--- /dev/null
+++ b/src/test/test1/ArrayAccess.java
@@ -0,0 +1,17 @@
+package test1;
+
+public class ArrayAccess {
+ int[] ia;
+ int[][] iaa;
+
+ public ArrayAccess() {
+ ia = new int[3];
+ iaa = new int[2][];
+ ia[0] = 3;
+ iaa[0] = ia;
+ }
+
+ public int test() {
+ return ia[0] + iaa[1][0];
+ }
+}
diff --git a/src/test/test1/BenchProceed.java b/src/test/test1/BenchProceed.java
new file mode 100644
index 0000000..c756181
--- /dev/null
+++ b/src/test/test1/BenchProceed.java
@@ -0,0 +1,83 @@
+package test1;
+
+class BenchProceed2 {
+ public void calc2() {
+ for (long i = 0; i < 10000000; ++i)
+ Math.sqrt(i);
+ }
+}
+
+public class BenchProceed {
+ public double d;
+ public java.lang.reflect.Method calcM;
+
+ public static final int N = 1000000;
+
+ public BenchProceed() throws Exception {
+ calcM = this.getClass().getDeclaredMethod("calc", new Class[0]);
+ }
+
+ public void calc() {
+ d = Math.sqrt(3.0);
+ }
+
+ public int p() {
+ long time = System.currentTimeMillis();
+ for (int i = N; i > 0; --i)
+ calc();
+
+ long time2 = System.currentTimeMillis();
+ return (int)(time2 - time);
+ }
+
+ public int q() {
+ long time = System.currentTimeMillis();
+ for (int i = N; i > 0; --i)
+ calc();
+
+ long time2 = System.currentTimeMillis();
+ return (int)(time2 - time);
+ }
+
+ public int s() {
+ BenchProceed2 bp = new BenchProceed2();
+ for (int i = 0; i < 5; ++i)
+ bp.calc2();
+
+ return 0;
+ }
+
+ public int t() {
+ BenchProceed2 bp = new BenchProceed2();
+ for (int i = 0; i < 5; ++i)
+ bp.calc2();
+
+ return 0;
+ }
+
+ public void before(Object[] args) {
+ }
+
+ public Object replace(Object[] args) {
+ try {
+ return calcM.invoke(this, args);
+ }
+ catch (Exception e) {
+ System.out.println(e);
+ }
+
+ return null;
+ }
+
+ public static void main(String[] args) throws Exception {
+ BenchProceed bp = new BenchProceed();
+ System.out.println("iteration " + N);
+ System.out.println("p (msec) " + bp.p());
+ System.out.println("q (msec) " + bp.q());
+ System.out.println("p (msec) " + bp.p());
+ System.out.println("q (msec) " + bp.q());
+
+ bp.s();
+ bp.t();
+ }
+}
diff --git a/src/test/test1/BenchProceedNew.java b/src/test/test1/BenchProceedNew.java
new file mode 100644
index 0000000..4b56be7
--- /dev/null
+++ b/src/test/test1/BenchProceedNew.java
@@ -0,0 +1,74 @@
+package test1;
+
+class BenchProceedNew2 {
+}
+
+class BenchProceedNew3 {
+ int p, q;
+ BenchProceedNew3(int i, int j) {
+ p = i; q = j;
+ }
+}
+
+public class BenchProceedNew {
+ public static final int N = 10000000;
+ Object result0;
+
+ public int org0() {
+ long time = System.currentTimeMillis();
+ Object obj = null;
+ for (int i = N; i > 0; --i)
+ obj = new BenchProceedNew2();
+
+ long time2 = System.currentTimeMillis();
+ result0 = obj;
+ return (int)(time2 - time);
+ }
+
+ public int jvst0() {
+ long time = System.currentTimeMillis();
+ Object obj = null;
+ for (int i = N; i > 0; --i)
+ obj = new BenchProceedNew2();
+
+ long time2 = System.currentTimeMillis();
+ result0 = obj;
+ return (int)(time2 - time);
+ }
+
+ public int org2() {
+ long time = System.currentTimeMillis();
+ Object obj = null;
+ for (int i = N; i > 0; --i)
+ obj = new BenchProceedNew3(i, i);
+
+ long time2 = System.currentTimeMillis();
+ result0 = obj;
+ return (int)(time2 - time);
+ }
+
+ public int jvst2() {
+ long time = System.currentTimeMillis();
+ Object obj = null;
+ for (int i = N; i > 0; --i)
+ obj = new BenchProceedNew3(i, i);
+
+ long time2 = System.currentTimeMillis();
+ result0 = obj;
+ return (int)(time2 - time);
+ }
+
+ public static void main(String[] args) throws Exception {
+ BenchProceedNew bp = new BenchProceedNew();
+ System.out.println("iteration " + N);
+ System.out.println("org0 (msec) " + bp.org0());
+ System.out.println("jvst0 (msec) " + bp.jvst0());
+ System.out.println("org2 (msec) " + bp.org2());
+ System.out.println("jvst2 (msec) " + bp.jvst2());
+
+ System.out.println("org0 (msec) " + bp.org0());
+ System.out.println("jvst0 (msec) " + bp.jvst0());
+ System.out.println("org2 (msec) " + bp.org2());
+ System.out.println("jvst2 (msec) " + bp.jvst2());
+ }
+}
diff --git a/src/test/test1/BenchStaticMethod.java b/src/test/test1/BenchStaticMethod.java
new file mode 100644
index 0000000..69abc30
--- /dev/null
+++ b/src/test/test1/BenchStaticMethod.java
@@ -0,0 +1,53 @@
+package test1;
+
+public class BenchStaticMethod {
+ public static final int N = 10000000;
+
+ public static int foo(int i) {
+ i /= 100000;
+ int f = 1;
+ while (i > 1)
+ f *= i--;
+
+ return f;
+ }
+
+ public static void foo2(int i) {}
+
+ public static int num = 0;
+
+ public static int test() {
+ long time = System.currentTimeMillis();
+ for (int i = N; i > 0; --i)
+ foo(i);
+
+ long time2 = System.currentTimeMillis();
+ return (int)(time2 - time);
+ }
+
+ public static int orgTest() {
+ long time = System.currentTimeMillis();
+ for (int i = N; i > 0; --i)
+ foo(i);
+
+ long time2 = System.currentTimeMillis();
+ return (int)(time2 - time);
+ }
+
+ public static int handTest() {
+ long time = System.currentTimeMillis();
+ for (int i = N; i > 0; --i) {
+ num += i;
+ foo(i);
+ }
+
+ long time2 = System.currentTimeMillis();
+ return (int)(time2 - time);
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.out.println("orgTest (msec) " + orgTest());
+ System.out.println("handTest (msec) " + handTest());
+ System.out.println("test (msec) " + test());
+ }
+}
diff --git a/src/test/test1/CalleeAfter.java b/src/test/test1/CalleeAfter.java
new file mode 100644
index 0000000..7d31d1b
--- /dev/null
+++ b/src/test/test1/CalleeAfter.java
@@ -0,0 +1,24 @@
+package test1;
+
+public class CalleeAfter {
+ public int p;
+
+ public CalleeAfter() {
+ p = 3;
+ }
+
+ public int m1(int i) {
+ return p + i;
+ }
+
+ public char m2(char c) {
+ return c;
+ }
+
+ public int test() {
+ if (m2('a') == 'b')
+ return m1(10);
+ else
+ return -1;
+ }
+}
diff --git a/src/test/test1/CalleeAfter2.java b/src/test/test1/CalleeAfter2.java
new file mode 100644
index 0000000..8a60c4d
--- /dev/null
+++ b/src/test/test1/CalleeAfter2.java
@@ -0,0 +1,50 @@
+package test1;
+
+public class CalleeAfter2 {
+ public int p;
+
+ public CalleeAfter2() {
+ p = 0;
+ }
+
+ public int m1(int i) {
+ return 0;
+ }
+
+ public void m2(int i) {
+ }
+
+ public String m3(int i) {
+ return null;
+ }
+
+ public String m4(int i) {
+ return null;
+ }
+
+ public int[] m5(int i) {
+ return null;
+ }
+
+ public int k1(int i) {
+ return 1;
+ }
+
+ public void k2(int i) {
+ p = 4;
+ }
+
+ public String k3(int i) {
+ return "ok";
+ }
+
+ public int[] k5(int i) {
+ return new int[2];
+ }
+
+ public int test() {
+ m2(0);
+ int q = m3(0).equals("ok") ? 10 : 20;
+ return m1(0) + p + q + m5(0).length;
+ }
+}
diff --git a/src/test/test1/CalleeAfter3.java b/src/test/test1/CalleeAfter3.java
new file mode 100644
index 0000000..068816c
--- /dev/null
+++ b/src/test/test1/CalleeAfter3.java
@@ -0,0 +1,39 @@
+package test1;
+
+public class CalleeAfter3 {
+ int value = 1;
+ public int m1(int k) {
+ if (k > 3)
+ return k;
+ else
+ return k + 1;
+ }
+
+ public String m2(int k) {
+ if (k > 3)
+ return "value" + k;
+ else
+ return "value" + value;
+ }
+
+ public void m3(int k) {
+ if (k > 3)
+ value += k;
+ else
+ value -= k;
+ }
+
+ public int m4(String obj) {
+ try {
+ return obj.length();
+ }
+ catch (NullPointerException e) {
+ return 0;
+ }
+ }
+
+ public int test() {
+ m3(5);
+ return m1(1) + m2(5).length() + value + m4("12345");
+ }
+}
diff --git a/src/test/test1/CalleeBefore.java b/src/test/test1/CalleeBefore.java
new file mode 100644
index 0000000..2e30b31
--- /dev/null
+++ b/src/test/test1/CalleeBefore.java
@@ -0,0 +1,39 @@
+package test1;
+
+class CalleeBeforeParent {
+ static int counter = 0;
+ int r;
+
+ CalleeBeforeParent(int k) {
+ System.out.println("CalleeBeforeParent:" + k);
+ r = counter;
+ }
+}
+
+public class CalleeBefore extends CalleeBeforeParent {
+ public int p;
+ public static int q;
+
+ public CalleeBefore() {
+ this(3);
+ }
+
+ public CalleeBefore(int k) {
+ super(k);
+ p = q = 0;
+ }
+
+ public int m1(int i) {
+ return p + i;
+ }
+
+ public static int m2(int i) {
+ return q + i;
+ }
+
+ public int getr() { return r; }
+
+ public int test() {
+ return m1(3) + m2(10);
+ }
+}
diff --git a/src/test/test1/CalleeCatch.java b/src/test/test1/CalleeCatch.java
new file mode 100644
index 0000000..f399aed
--- /dev/null
+++ b/src/test/test1/CalleeCatch.java
@@ -0,0 +1,17 @@
+package test1;
+
+public class CalleeCatch {
+ public int p;
+
+ public CalleeCatch() {
+ p = 3;
+ }
+
+ public int m1(int i) throws Exception {
+ throw new Exception();
+ }
+
+ public int test() throws Exception {
+ return m1(p);
+ }
+}
diff --git a/src/test/test1/Cflow.java b/src/test/test1/Cflow.java
new file mode 100644
index 0000000..a5c73f3
--- /dev/null
+++ b/src/test/test1/Cflow.java
@@ -0,0 +1,40 @@
+package test1;
+
+public class Cflow {
+ public int run() {
+ return k1(4);
+ }
+
+ public int run2() {
+ return fact(5);
+ }
+
+ public int fact(int n) {
+ if (n <= 1)
+ return n;
+ else
+ return n * fact(n - 1);
+ }
+
+ public int k1(int i) {
+ if (i > 1)
+ return k2(i - 1);
+ else if (i == 1)
+ return i;
+ else if (i == 0)
+ throw new RuntimeException();
+ else
+ return -i;
+ }
+
+ public int k2(int i) {
+ if (i > 1)
+ return k1(i - 1);
+ else if (i == 1)
+ return i;
+ else if (i == 0)
+ throw new RuntimeException();
+ else
+ return -i;
+ }
+}
diff --git a/src/test/test1/Clinit.java b/src/test/test1/Clinit.java
new file mode 100644
index 0000000..076983b
--- /dev/null
+++ b/src/test/test1/Clinit.java
@@ -0,0 +1,8 @@
+package test1;
+
+public class Clinit {
+ public int i = 123;
+ public static int j = 456;
+
+ public int run() { return j; }
+}
diff --git a/src/test/test1/Clinit2.java b/src/test/test1/Clinit2.java
new file mode 100644
index 0000000..0f19a8f
--- /dev/null
+++ b/src/test/test1/Clinit2.java
@@ -0,0 +1,8 @@
+package test1;
+
+public class Clinit2 {
+ public int i = 123;
+ public static int j;
+
+ public int run() { return j; }
+}
diff --git a/src/test/test1/CodeConv.java b/src/test/test1/CodeConv.java
new file mode 100644
index 0000000..76cc010
--- /dev/null
+++ b/src/test/test1/CodeConv.java
@@ -0,0 +1,25 @@
+package test1;
+
+class CodeConvP {
+ private int a1 = 7;
+ protected int a2 = 11;
+ protected int a3 = 13;
+ protected int a4 = 17;
+
+ protected int b1 = 3;
+
+ public static int getA1(Object t) { return 23; }
+
+ public static int getA2(Object t) { return 27; }
+
+ public static void putB1(Object t, int v) { ((CodeConvP)t).b1 = 5; }
+}
+
+public class CodeConv extends CodeConvP {
+ private String a1 = "a1";
+
+ public int run() {
+ b1 = 0;
+ return b1 + a1.length() + a2 + a3;
+ }
+}
diff --git a/src/test/test1/DefineClassCapability.java b/src/test/test1/DefineClassCapability.java
new file mode 100644
index 0000000..fe386f0
--- /dev/null
+++ b/src/test/test1/DefineClassCapability.java
@@ -0,0 +1,7 @@
+/*
+ * This is used as a capability for running CtClass#toClass().
+ */
+package test1;
+
+public class DefineClassCapability {
+}
diff --git a/src/test/test1/Delegator.java b/src/test/test1/Delegator.java
new file mode 100644
index 0000000..9ca93b8
--- /dev/null
+++ b/src/test/test1/Delegator.java
@@ -0,0 +1,12 @@
+package test1;
+
+class SuperDelegator {
+ public int f(int p) { return p + 1; }
+ public static int g(int p) { return p + 1; }
+}
+
+public class Delegator extends SuperDelegator {
+ public int run() {
+ return f(3) + g(10);
+ }
+}
diff --git a/src/test/test1/Dispatch.java b/src/test/test1/Dispatch.java
new file mode 100644
index 0000000..0ee8b3f
--- /dev/null
+++ b/src/test/test1/Dispatch.java
@@ -0,0 +1,15 @@
+package test1;
+
+public class Dispatch {
+ public int run() {
+ return 5;
+ }
+
+ public int f(Object obj) {
+ return 1;
+ }
+
+ public int f(Object[] obj) {
+ return 2;
+ }
+}
diff --git a/src/test/test1/DollarClass.java b/src/test/test1/DollarClass.java
new file mode 100644
index 0000000..74e6c38
--- /dev/null
+++ b/src/test/test1/DollarClass.java
@@ -0,0 +1,21 @@
+package test1;
+
+public class DollarClass {
+ int f = 1;
+
+ public int run() {
+ return k1(4) + k2(3);
+ }
+
+ public int k1(int i) {
+ return i;
+ }
+
+ public static int k2(int i) {
+ return i + k3(i);
+ }
+
+ public static int k3(int i) {
+ return i;
+ }
+}
diff --git a/src/test/test1/EmptyBody.java b/src/test/test1/EmptyBody.java
new file mode 100644
index 0000000..9a09853
--- /dev/null
+++ b/src/test/test1/EmptyBody.java
@@ -0,0 +1,20 @@
+package test1;
+
+public abstract class EmptyBody {
+ public EmptyBody() {}
+ public EmptyBody(int i) {}
+ public EmptyBody(String i) { System.out.println(i); }
+ public EmptyBody(double d) { this(3); }
+
+ public void m1(int i, int j) {}
+ public void m2(int i, int j) {
+ try { return; }
+ catch (Exception e) {}
+ }
+
+ public int m3(int k) { return 0; }
+
+ public native int m4();
+
+ public abstract int m5();
+}
diff --git a/src/test/test1/ExprEdit.java b/src/test/test1/ExprEdit.java
new file mode 100644
index 0000000..761712f
--- /dev/null
+++ b/src/test/test1/ExprEdit.java
@@ -0,0 +1,11 @@
+package test1;
+
+public class ExprEdit {
+ public int k1(int j) { return j; }
+
+ public static int k2(int j) { return j; }
+
+ public void k3(int j) { System.out.println("k3: " + j); }
+
+ public int k0() { k3(1); return k1(3) + k2(7); }
+}
diff --git a/src/test/test1/ExprEdit2.java b/src/test/test1/ExprEdit2.java
new file mode 100644
index 0000000..86b107b
--- /dev/null
+++ b/src/test/test1/ExprEdit2.java
@@ -0,0 +1,8 @@
+package test1;
+
+public class ExprEdit2 {
+ int df;
+ static int sf;
+
+ public int k1() { df = 3; sf = 7; return df + sf; }
+}
diff --git a/src/test/test1/ExprEdit3.java b/src/test/test1/ExprEdit3.java
new file mode 100644
index 0000000..ecfcc67
--- /dev/null
+++ b/src/test/test1/ExprEdit3.java
@@ -0,0 +1,13 @@
+package test1;
+
+public class ExprEdit3 {
+ int value;
+
+ private int f() { return 3; }
+
+ public ExprEdit3() { value = 0; }
+
+ public ExprEdit3(ExprEdit3 e, int i) { value = i; }
+
+ public int k1() { return new ExprEdit3(null, f()).value; }
+}
diff --git a/src/test/test1/ExprEdit4.java b/src/test/test1/ExprEdit4.java
new file mode 100644
index 0000000..6f76447
--- /dev/null
+++ b/src/test/test1/ExprEdit4.java
@@ -0,0 +1,16 @@
+package test1;
+
+public class ExprEdit4 {
+ int value;
+
+ void f() { value = 3; }
+
+ public ExprEdit4() { value = 0; }
+
+ public int k1() {
+ ExprEdit4 e = null;
+ e.f();
+ e = null;
+ return new ExprEdit4() == null ? 1 : 0;
+ }
+}
diff --git a/src/test/test1/ExprEdit5.java b/src/test/test1/ExprEdit5.java
new file mode 100644
index 0000000..931f4c7
--- /dev/null
+++ b/src/test/test1/ExprEdit5.java
@@ -0,0 +1,14 @@
+package test1;
+
+public class ExprEdit5 {
+ int value;
+
+ public ExprEdit5() { value = 0; }
+
+ public ExprEdit5(String s) { value = 1; }
+
+ public int k1() {
+ ExprEdit5 e = new ExprEdit5();
+ return e.value;
+ }
+}
diff --git a/src/test/test1/ExprEdit6.java b/src/test/test1/ExprEdit6.java
new file mode 100644
index 0000000..8db5f72
--- /dev/null
+++ b/src/test/test1/ExprEdit6.java
@@ -0,0 +1,15 @@
+package test1;
+
+public class ExprEdit6 {
+ int value;
+
+ public ExprEdit6() { value = 0; }
+
+ public int k2() {
+ return value;
+ }
+
+ public int k1() {
+ return k2();
+ }
+}
diff --git a/src/test/test1/ExprEdit7.java b/src/test/test1/ExprEdit7.java
new file mode 100644
index 0000000..03be6d8
--- /dev/null
+++ b/src/test/test1/ExprEdit7.java
@@ -0,0 +1,30 @@
+package test1;
+
+public class ExprEdit7 {
+ int value;
+ Class c1, c2;
+
+ public ExprEdit7() { value = 0; }
+
+ public boolean k2(Object obj) {
+ return obj instanceof ExprEdit7;
+ }
+
+ public ExprEdit7 k3(Object obj) {
+ return (ExprEdit7)obj;
+ }
+
+ public int k1() {
+ ExprEdit7 e = new ExprEdit7();
+ if (k2(e))
+ k3(e).value = 3;
+ else
+ k3(e).value = 7;
+
+ System.out.println("ExprEdit7: " + c1.getName());
+ if (c1 == c2 && c1.getName().equals("test1.ExprEdit7"))
+ return e.value;
+ else
+ return e.value - 1;
+ }
+}
diff --git a/src/test/test1/ExprEdit8.java b/src/test/test1/ExprEdit8.java
new file mode 100644
index 0000000..a38b744
--- /dev/null
+++ b/src/test/test1/ExprEdit8.java
@@ -0,0 +1,16 @@
+package test1;
+
+class ExprSuper8 {
+ int value;
+ ExprSuper8(int i) {
+ value = i;
+ }
+}
+
+public class ExprEdit8 extends ExprSuper8 {
+ public ExprEdit8() { super(3); }
+
+ public int k1() {
+ return value;
+ }
+}
diff --git a/src/test/test1/FieldInit.java b/src/test/test1/FieldInit.java
new file mode 100644
index 0000000..ee48314
--- /dev/null
+++ b/src/test/test1/FieldInit.java
@@ -0,0 +1,24 @@
+package test1;
+
+public class FieldInit {
+ public static int counter = 0;
+ public static int loop = 3;
+
+ public static int get(Object obj) {
+ System.out.println("FieldInit: get");
+ return ++counter;
+ }
+
+ public FieldInit() {
+ do {
+ --loop;
+ } while (loop > 0);
+ }
+
+ public static class FI {
+ public FieldInit fi;
+ public FI(FieldInit fi) {
+ this.fi = fi;
+ }
+ }
+}
diff --git a/src/test/test1/FieldInit2.java b/src/test/test1/FieldInit2.java
new file mode 100644
index 0000000..3260b0f
--- /dev/null
+++ b/src/test/test1/FieldInit2.java
@@ -0,0 +1,21 @@
+package test1;
+
+public class FieldInit2 {
+ public static int counter = 0;
+ public static int loop = 3;
+
+ public static int get(Object obj) throws Exception {
+ throw new Exception();
+ }
+
+ public FieldInit2() {
+ try {
+ do {
+ --loop;
+ } while (loop > 0);
+ }
+ catch (Exception e) {
+ System.out.println("FieldInit2: catch");
+ }
+ }
+}
diff --git a/src/test/test1/FieldMod.java b/src/test/test1/FieldMod.java
new file mode 100644
index 0000000..2d68b33
--- /dev/null
+++ b/src/test/test1/FieldMod.java
@@ -0,0 +1,7 @@
+package test1;
+
+public class FieldMod {
+ @SuppressWarnings("unused")
+ private String text;
+ public int i;
+}
diff --git a/src/test/test1/Freeze.java b/src/test/test1/Freeze.java
new file mode 100644
index 0000000..80676e7
--- /dev/null
+++ b/src/test/test1/Freeze.java
@@ -0,0 +1,4 @@
+package test1;
+
+public class Freeze {
+}
diff --git a/src/test/test1/GetThrowables.java b/src/test/test1/GetThrowables.java
new file mode 100644
index 0000000..11ff3e0
--- /dev/null
+++ b/src/test/test1/GetThrowables.java
@@ -0,0 +1,47 @@
+package test1;
+
+class GetThrow1 extends Exception {
+
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+}
+
+class GetThrow2 extends Exception {
+
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+}
+
+public class GetThrowables {
+ int k = 0;
+
+ public void m1() throws GetThrow1, GetThrow2 {
+ if (k < 0)
+ throw new GetThrow1();
+ else if (k == 1)
+ throw new GetThrow2();
+
+ k = 1;
+ }
+
+ public int run() throws GetThrow2 {
+ int i = 0;
+ try {
+ try {
+ m1();
+ }
+ catch (GetThrow1 e) {
+ i = 1;
+ throw e;
+ }
+ finally {
+ i += 3;
+ }
+ }
+ catch (GetThrow1 e2) {
+ ++i;
+ }
+
+ return i;
+ }
+}
diff --git a/src/test/test1/Handler.java b/src/test/test1/Handler.java
new file mode 100644
index 0000000..1785551
--- /dev/null
+++ b/src/test/test1/Handler.java
@@ -0,0 +1,29 @@
+package test1;
+
+public class Handler {
+ public int p;
+
+ public Handler() {
+ p = 3;
+ }
+
+ public int m1(int i) {
+ p = 1;
+ try {
+ try {
+ if (i < 0)
+ throw new IndexOutOfBoundsException();
+ else if (i == 0)
+ throw new ClassNotFoundException();
+ }
+ catch (IndexOutOfBoundsException e) {}
+ }
+ catch (ClassNotFoundException e) {}
+
+ return p;
+ }
+
+ public int test() {
+ return m1(1) + m1(0) + m1(-1);
+ }
+}
diff --git a/src/test/test1/Howard.java b/src/test/test1/Howard.java
new file mode 100644
index 0000000..6d20380
--- /dev/null
+++ b/src/test/test1/Howard.java
@@ -0,0 +1,30 @@
+package test1;
+
+import java.io.IOException;
+
+class Howard4 implements HowardHome {
+ int n = 0;
+ public Object create() throws IOException {
+ if (n == 1)
+ throw new IOException();
+ else
+ return "howard4";
+ }
+}
+
+interface HowardHome {
+ Object create() throws IOException;
+}
+
+class Howard2 {
+ Object lookup(String n) { return new Howard4(); }
+}
+
+public class Howard extends Howard2 {
+ @SuppressWarnings("unused")
+ private Object _remote;
+
+ public int run() {
+ return 0;
+ }
+}
diff --git a/src/test/test1/InvokeInt.java b/src/test/test1/InvokeInt.java
new file mode 100644
index 0000000..22eb5f1
--- /dev/null
+++ b/src/test/test1/InvokeInt.java
@@ -0,0 +1,19 @@
+package test1;
+
+interface InvokeInt2 {
+ int k(int i);
+}
+
+public class InvokeInt implements InvokeInt2 {
+ public int run() {
+ return check(this);
+ }
+
+ public int check(InvokeInt2 obj) {
+ return obj.k(3);
+ }
+
+ public int k(int i) {
+ return i + 1;
+ }
+}
diff --git a/src/test/test1/LineNumber.java b/src/test/test1/LineNumber.java
new file mode 100644
index 0000000..46d3d12
--- /dev/null
+++ b/src/test/test1/LineNumber.java
@@ -0,0 +1,46 @@
+package test1;
+
+import java.io.*;
+
+public class LineNumber {
+ public static void sort(int[] data) {
+ int i, j;
+ for (i = 0; i < data.length - 1; ++i) {
+ int k = i;
+ int p = data[k];
+ for (j = i + 1; j < data.length; ++j)
+ if (p > data[j]) {
+ k = j;
+ p = data[k];
+ }
+
+ data[k] = data[i];
+ data[i] = p;
+ }
+ }
+
+ public int f(int i) {
+ i = i + 3;
+ return i;
+ }
+
+ public static void main(String[] args) throws Exception {
+ int i;
+ int data[] = new int[Integer.parseInt(args[0])];
+
+ BufferedReader r = new BufferedReader(new FileReader(args[1]));
+ for (i = 0; i < data.length; ++i) {
+ String value = r.readLine();
+ data[i] = Integer.parseInt(value);
+ }
+
+ r.close();
+ sort(data);
+ PrintWriter out =
+ new PrintWriter(new BufferedWriter(new FileWriter(args[2])));
+ for (i = 0; i < data.length; ++i)
+ out.println(data[i]);
+
+ out.close();
+ }
+}
diff --git a/src/test/test1/LocalVars.java b/src/test/test1/LocalVars.java
new file mode 100644
index 0000000..a3d14d7
--- /dev/null
+++ b/src/test/test1/LocalVars.java
@@ -0,0 +1,18 @@
+package test1;
+
+public class LocalVars {
+ public int poi(String str) {
+ Object obj = str;
+ int hash = obj.hashCode();
+ return hash;
+ }
+
+ public int foo(int i) {
+ int j = 3;
+ if (i == 0) {
+ String s = "zero";
+ j += s.length();
+ }
+ return j + 3;
+ }
+}
diff --git a/src/test/test1/MakeClass.java b/src/test/test1/MakeClass.java
new file mode 100644
index 0000000..e6d6b50
--- /dev/null
+++ b/src/test/test1/MakeClass.java
@@ -0,0 +1,9 @@
+package test1;
+
+public class MakeClass {
+ public int p;
+
+ public MakeClass() {
+ p = 3;
+ }
+}
diff --git a/src/test/test1/MySerializableClass.java b/src/test/test1/MySerializableClass.java
new file mode 100644
index 0000000..975dd65
--- /dev/null
+++ b/src/test/test1/MySerializableClass.java
@@ -0,0 +1,36 @@
+package test1;
+
+import java.io.*;
+
+/**
+ *
+ * @author Bob Lee
+ */
+public class MySerializableClass implements Serializable, Cloneable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+ String fieldA;
+ String fieldB;
+
+ public MySerializableClass() { fieldA = null; }
+
+ public MySerializableClass(String k) { fieldA = k; }
+
+ public MySerializableClass(int k) { fieldA = null; }
+
+ public String getFieldA() {
+ return fieldB;
+ }
+
+ public String getFieldA(int i) { return fieldB; }
+
+ public String getFieldA(int i, int j) { return fieldB; }
+
+ public String getFieldB() {
+ return fieldB;
+ }
+
+ public void doSomething(Object o) {
+ }
+
+}
diff --git a/src/test/test1/NewInterface.java b/src/test/test1/NewInterface.java
new file mode 100644
index 0000000..c604e48
--- /dev/null
+++ b/src/test/test1/NewInterface.java
@@ -0,0 +1,5 @@
+package test1;
+
+public class NewInterface {
+ public int foo() { return 3; }
+}
diff --git a/src/test/test1/Pac.java b/src/test/test1/Pac.java
new file mode 100644
index 0000000..3e99596
--- /dev/null
+++ b/src/test/test1/Pac.java
@@ -0,0 +1,15 @@
+package test1;
+
+class Pac2 {
+ int p, q;
+}
+
+public class Pac {
+ int x;
+ public int run() {
+ Class c = Pac2.class;
+ Package p = c.getPackage();
+ System.out.println(p);
+ return p == null ? 0 : 1;
+ }
+}
diff --git a/src/test/test1/Point.java b/src/test/test1/Point.java
new file mode 100644
index 0000000..1f88159
--- /dev/null
+++ b/src/test/test1/Point.java
@@ -0,0 +1,5 @@
+package test1;
+
+public class Point {
+ public int x, y;
+}
diff --git a/src/test/test1/Proceed.java b/src/test/test1/Proceed.java
new file mode 100644
index 0000000..a55128f
--- /dev/null
+++ b/src/test/test1/Proceed.java
@@ -0,0 +1,12 @@
+package test1;
+
+@SuppressWarnings("unused")
+public class Proceed {
+ public int p(int i, int j) { return i + j; }
+
+ public int k1(int j) { return j; }
+
+ private int k2(int j) { return j + 1; }
+
+ private static Proceed another = new Proceed();
+}
diff --git a/src/test/test1/Proceed2.java b/src/test/test1/Proceed2.java
new file mode 100644
index 0000000..b66eb0a
--- /dev/null
+++ b/src/test/test1/Proceed2.java
@@ -0,0 +1,26 @@
+package test1;
+
+public class Proceed2 {
+ public Proceed2() { i = 3; }
+
+ public int i;
+
+ public void p() {}
+
+ public int k1() {
+ Proceed2 p2 = new Proceed2();
+ boolean b = p2 instanceof Proceed2;
+ Object obj = p2;
+ Proceed2 q2 = (Proceed2)obj;
+ p2.p();
+ i = 2;
+ return i;
+ }
+
+ public int k2() {
+ Proceed2 p2 = new Proceed2();
+ p2.p();
+ i = 2;
+ return i;
+ }
+}
diff --git a/src/test/test1/Proceed3.java b/src/test/test1/Proceed3.java
new file mode 100644
index 0000000..7cee996
--- /dev/null
+++ b/src/test/test1/Proceed3.java
@@ -0,0 +1,9 @@
+package test1;
+
+public class Proceed3 {
+ public int p(int i) { return i; }
+
+ public int k1() {
+ return p(3);
+ }
+}
diff --git a/src/test/test1/RenameClass.java b/src/test/test1/RenameClass.java
new file mode 100644
index 0000000..93cfc23
--- /dev/null
+++ b/src/test/test1/RenameClass.java
@@ -0,0 +1,27 @@
+package test1;
+
+import java.util.Hashtable;
+
+class RenameClass2 {
+ String name;
+}
+
+@SuppressWarnings({"rawtypes","unchecked", "unused"})
+public class RenameClass {
+ private Hashtable table;
+ public RenameClass() {
+ table = new Hashtable();
+ }
+
+ public int test() {
+ say();
+ return 0;
+ }
+
+ public void say() {
+ RenameClass2[] pair = new RenameClass2[2];
+ pair[0] = new RenameClass2();
+ table.put("Muga", pair);
+ RenameClass2[] p = (RenameClass2[]) table.get("Muga");
+ }
+}
diff --git a/src/test/test1/SetBody.java b/src/test/test1/SetBody.java
new file mode 100644
index 0000000..7849b27
--- /dev/null
+++ b/src/test/test1/SetBody.java
@@ -0,0 +1,9 @@
+package test1;
+
+public class SetBody {
+ int value;
+ public int m1(int i, int j) { return i + j; }
+ public void m2(int k) {}
+ public void m3(int k) {}
+ public int run() { m2(3); return m1(3, 4); }
+}
diff --git a/src/test/test1/SetName.java b/src/test/test1/SetName.java
new file mode 100644
index 0000000..95e283e
--- /dev/null
+++ b/src/test/test1/SetName.java
@@ -0,0 +1,6 @@
+package test1;
+
+public class SetName {
+ public int i;
+ public int foo(SetName sn) { return 1; }
+}
diff --git a/src/test/test1/SigType.java b/src/test/test1/SigType.java
new file mode 100644
index 0000000..6fa03f3
--- /dev/null
+++ b/src/test/test1/SigType.java
@@ -0,0 +1,21 @@
+package test1;
+
+public class SigType {
+ int f = 1;
+
+ public int run() {
+ return k1(4, "test") + k2(3);
+ }
+
+ public int k1(int i, String s) {
+ return i + s.length();
+ }
+
+ public int k2(int i) {
+ return f + k3(i);
+ }
+
+ public int k3(int i) {
+ return i + 1;
+ }
+}
diff --git a/src/test/test1/StaticConsBody.java b/src/test/test1/StaticConsBody.java
new file mode 100644
index 0000000..8060763
--- /dev/null
+++ b/src/test/test1/StaticConsBody.java
@@ -0,0 +1,18 @@
+package test1;
+
+public class StaticConsBody {
+ public static int i;
+ public int j;
+
+ static {
+ i = 3;
+ }
+
+ public StaticConsBody() {
+ j = 2;
+ }
+
+ public int run() {
+ return i + j;
+ }
+}
diff --git a/src/test/test1/StaticField.java b/src/test/test1/StaticField.java
new file mode 100644
index 0000000..e792402
--- /dev/null
+++ b/src/test/test1/StaticField.java
@@ -0,0 +1,5 @@
+package test1;
+
+public class StaticField {
+ public static int counter;
+}
diff --git a/src/test/test1/Subtype.java b/src/test/test1/Subtype.java
new file mode 100644
index 0000000..dffd1eb
--- /dev/null
+++ b/src/test/test1/Subtype.java
@@ -0,0 +1,17 @@
+package test1;
+
+public class Subtype extends SubtypeA implements SubtypeC {
+ int i;
+}
+
+class SubtypeA {
+ int a;
+}
+
+interface SubtypeB {
+ final int b = 3;
+}
+
+interface SubtypeC extends SubtypeB {
+ final int c = 4;
+}
diff --git a/src/test/test1/TryCatch.java b/src/test/test1/TryCatch.java
new file mode 100644
index 0000000..72f2107
--- /dev/null
+++ b/src/test/test1/TryCatch.java
@@ -0,0 +1,34 @@
+package test1;
+
+public class TryCatch {
+ int a = 0;
+ String s = null;
+
+ public void init() {
+ s = "test";
+ }
+
+ public void doit() {
+ a = s.length();
+ }
+
+ public void m2() {}
+
+ public int m1() {
+ m2();
+ return a;
+ }
+
+ public int p1() {
+ try {
+ return s.length();
+ }
+ catch (NullPointerException e) {
+ throw e;
+ }
+ }
+
+ public int run() {
+ return m1();
+ }
+}
diff --git a/src/test/test2/AddCatchForConstructor.class b/src/test/test2/AddCatchForConstructor.class
new file mode 100644
index 0000000..9b5e937
--- /dev/null
+++ b/src/test/test2/AddCatchForConstructor.class
Binary files differ
diff --git a/src/test/test2/AddCatchForConstructor.java b/src/test/test2/AddCatchForConstructor.java
new file mode 100644
index 0000000..f604f99
--- /dev/null
+++ b/src/test/test2/AddCatchForConstructor.java
@@ -0,0 +1,13 @@
+package test2;
+
+public class AddCatchForConstructor {
+ int i;
+ public AddCatchForConstructor() {
+ this(3);
+ i++;
+ }
+
+ public AddCatchForConstructor(int k) {
+ i = k;
+ }
+}
diff --git a/src/test/test2/AddLocalVar.class b/src/test/test2/AddLocalVar.class
new file mode 100644
index 0000000..ad5f1d9
--- /dev/null
+++ b/src/test/test2/AddLocalVar.class
Binary files differ
diff --git a/src/test/test2/AddLocalVar.java b/src/test/test2/AddLocalVar.java
new file mode 100644
index 0000000..7885b65
--- /dev/null
+++ b/src/test/test2/AddLocalVar.java
@@ -0,0 +1,8 @@
+package test2;
+
+public class AddLocalVar {
+ public int foo() {
+ int j = 1;
+ return j;
+ }
+}
diff --git a/src/test/test2/AddMethod.java b/src/test/test2/AddMethod.java
new file mode 100644
index 0000000..c04ff60
--- /dev/null
+++ b/src/test/test2/AddMethod.java
@@ -0,0 +1,10 @@
+package test2;
+
+public class AddMethod {
+ @SuppressWarnings("unused")
+ private int f;
+
+ public int f() { return 0; }
+ public AddMethod() {}
+ public int f(int i) { return i; }
+}
diff --git a/src/test/test2/Anon.java b/src/test/test2/Anon.java
new file mode 100644
index 0000000..a9c0773
--- /dev/null
+++ b/src/test/test2/Anon.java
@@ -0,0 +1,19 @@
+package test2;
+
+@SuppressWarnings("unused")
+public class Anon {
+ public Object make() {
+ return new Object() { int k; };
+ }
+
+ public static class Anon2 {
+ Object obj;
+ public Anon2() {
+ obj = new Object() { int k; };
+ }
+ }
+
+ public static class Anon3 {
+ public static Object sobj = new Object() { int p; };
+ }
+}
diff --git a/src/test/test2/ArrayAndNull.java b/src/test/test2/ArrayAndNull.java
new file mode 100644
index 0000000..52b8b97
--- /dev/null
+++ b/src/test/test2/ArrayAndNull.java
@@ -0,0 +1,9 @@
+package test2;
+
+public class ArrayAndNull {
+ public int[] test() {
+ int[] arr = { 1, 2 };
+ arr = null;
+ return arr;
+ }
+}
diff --git a/src/test/test2/ArrayInit.class b/src/test/test2/ArrayInit.class
new file mode 100644
index 0000000..1786a0e
--- /dev/null
+++ b/src/test/test2/ArrayInit.class
Binary files differ
diff --git a/src/test/test2/ArrayLenTest.class b/src/test/test2/ArrayLenTest.class
new file mode 100644
index 0000000..0987cc0
--- /dev/null
+++ b/src/test/test2/ArrayLenTest.class
Binary files differ
diff --git a/src/test/test2/ArrayLenTest.java b/src/test/test2/ArrayLenTest.java
new file mode 100644
index 0000000..52fe85f
--- /dev/null
+++ b/src/test/test2/ArrayLenTest.java
@@ -0,0 +1,5 @@
+package test2;
+
+public class ArrayLenTest {
+ public int length = 1;
+}
diff --git a/src/test/test2/ArrayLength.class b/src/test/test2/ArrayLength.class
new file mode 100644
index 0000000..9577874
--- /dev/null
+++ b/src/test/test2/ArrayLength.class
Binary files differ
diff --git a/src/test/test2/Brennan.class b/src/test/test2/Brennan.class
new file mode 100644
index 0000000..9c7f395
--- /dev/null
+++ b/src/test/test2/Brennan.class
Binary files differ
diff --git a/src/test/test2/Brennan.java b/src/test/test2/Brennan.java
new file mode 100644
index 0000000..4cc0a50
--- /dev/null
+++ b/src/test/test2/Brennan.java
@@ -0,0 +1,6 @@
+package test2;
+
+@SuppressWarnings("unused")
+public class Brennan {
+ private Object format = null;
+}
diff --git a/src/test/test2/CodeGen.class b/src/test/test2/CodeGen.class
new file mode 100644
index 0000000..65c24e6
--- /dev/null
+++ b/src/test/test2/CodeGen.class
Binary files differ
diff --git a/src/test/test2/CodeGen.java b/src/test/test2/CodeGen.java
new file mode 100644
index 0000000..d3df0d8
--- /dev/null
+++ b/src/test/test2/CodeGen.java
@@ -0,0 +1,15 @@
+package test2;
+
+public class CodeGen {
+ public String msg = "";
+ public int seven() { return 7; }
+
+ public String seven(String s) { return s + 7; }
+
+ public String six(String s) { return s + 6; }
+
+ public int run() {
+ System.out.println("done.");
+ return msg.length();
+ }
+}
diff --git a/src/test/test2/CodeGen2.class b/src/test/test2/CodeGen2.class
new file mode 100644
index 0000000..fea25cc
--- /dev/null
+++ b/src/test/test2/CodeGen2.class
Binary files differ
diff --git a/src/test/test2/ConstBody.class b/src/test/test2/ConstBody.class
new file mode 100644
index 0000000..41175d1
--- /dev/null
+++ b/src/test/test2/ConstBody.class
Binary files differ
diff --git a/src/test/test2/ConstBody.java b/src/test/test2/ConstBody.java
new file mode 100644
index 0000000..3fad860
--- /dev/null
+++ b/src/test/test2/ConstBody.java
@@ -0,0 +1,22 @@
+package test2;
+
+class ConstBody2 {
+ int value;
+ ConstBody2() {
+ value = 1;
+ }
+
+ ConstBody2(String s, Integer i) {
+ value = 2;
+ }
+}
+
+public class ConstBody extends ConstBody2 {
+ public ConstBody() {
+ super();
+ }
+
+ public int bar() {
+ return value;
+ }
+}
diff --git a/src/test/test2/ConstField.class b/src/test/test2/ConstField.class
new file mode 100644
index 0000000..5f7a4d8
--- /dev/null
+++ b/src/test/test2/ConstField.class
Binary files differ
diff --git a/src/test/test2/ConstField.java b/src/test/test2/ConstField.java
new file mode 100644
index 0000000..2d08cfc
--- /dev/null
+++ b/src/test/test2/ConstField.java
@@ -0,0 +1,13 @@
+package test2;
+
+public class ConstField {
+ public static final boolean b = true;
+ public static final int i = 3;
+ public static final long j = 7L;
+ public static final float f = 8.0F;
+ public static final double d = 9.0;
+ public static final String s = "const";
+ public static final Object obj = null;
+ public static final Integer integer = Integer.valueOf(4);
+ public static int k = 2;
+}
diff --git a/src/test/test2/Construct.java b/src/test/test2/Construct.java
new file mode 100644
index 0000000..250a55c
--- /dev/null
+++ b/src/test/test2/Construct.java
@@ -0,0 +1,7 @@
+package test2;
+
+public class Construct {
+ public Construct(int i) {}
+ public Construct() {}
+ static int i = 3;
+}
diff --git a/src/test/test2/DefineClassCapability.java b/src/test/test2/DefineClassCapability.java
new file mode 100644
index 0000000..303ffcc
--- /dev/null
+++ b/src/test/test2/DefineClassCapability.java
@@ -0,0 +1,7 @@
+/*
+ * This is used as a capability for running CtClass#toClass().
+ */
+package test2;
+
+public class DefineClassCapability {
+}
diff --git a/src/test/test2/DotClass.class b/src/test/test2/DotClass.class
new file mode 100644
index 0000000..6cc7a6b
--- /dev/null
+++ b/src/test/test2/DotClass.class
Binary files differ
diff --git a/src/test/test2/DotClass2.class b/src/test/test2/DotClass2.class
new file mode 100644
index 0000000..aa67fb1
--- /dev/null
+++ b/src/test/test2/DotClass2.class
Binary files differ
diff --git a/src/test/test2/DotClass4.class b/src/test/test2/DotClass4.class
new file mode 100644
index 0000000..48e51b7
--- /dev/null
+++ b/src/test/test2/DotClass4.class
Binary files differ
diff --git a/src/test/test2/Finally.class b/src/test/test2/Finally.class
new file mode 100644
index 0000000..f680dbc
--- /dev/null
+++ b/src/test/test2/Finally.class
Binary files differ
diff --git a/src/test/test2/Finally.java b/src/test/test2/Finally.java
new file mode 100644
index 0000000..1da8572
--- /dev/null
+++ b/src/test/test2/Finally.java
@@ -0,0 +1,12 @@
+package test2;
+
+public class Finally {
+ int a = 0;
+ String s = null;
+ double b = 1.0;
+
+ public void update() {
+ a = s.length();
+ b++;
+ }
+}
diff --git a/src/test/test2/Imported.class b/src/test/test2/Imported.class
new file mode 100644
index 0000000..3ae65fd
--- /dev/null
+++ b/src/test/test2/Imported.class
Binary files differ
diff --git a/src/test/test2/Importer.class b/src/test/test2/Importer.class
new file mode 100644
index 0000000..24da5de
--- /dev/null
+++ b/src/test/test2/Importer.class
Binary files differ
diff --git a/src/test/test2/IncOp.class b/src/test/test2/IncOp.class
new file mode 100644
index 0000000..f75a9eb
--- /dev/null
+++ b/src/test/test2/IncOp.class
Binary files differ
diff --git a/src/test/test2/Inherit.class b/src/test/test2/Inherit.class
new file mode 100644
index 0000000..ef886ce
--- /dev/null
+++ b/src/test/test2/Inherit.class
Binary files differ
diff --git a/src/test/test2/Inherit.java b/src/test/test2/Inherit.java
new file mode 100644
index 0000000..b310730
--- /dev/null
+++ b/src/test/test2/Inherit.java
@@ -0,0 +1,26 @@
+package test2;
+
+interface Inherit1 {
+ void foo1();
+}
+
+interface Inherit2 extends Inherit1 {
+ void foo2();
+}
+
+abstract class Inherit3 implements Inherit2 {
+ abstract void foo3();
+}
+
+public class Inherit extends Inherit3 {
+ public void foo1() { System.out.println("foo1"); }
+ public void foo2() { System.out.println("foo2"); }
+ public void foo3() { System.out.println("foo3"); }
+
+ public static void main(String args[]) {
+ Inherit i = new Inherit();
+ Inherit2 i2 = i;
+ i.foo2();
+ i2.foo1();
+ }
+}
diff --git a/src/test/test2/Inner.class b/src/test/test2/Inner.class
new file mode 100644
index 0000000..d7506a6
--- /dev/null
+++ b/src/test/test2/Inner.class
Binary files differ
diff --git a/src/test/test2/Inner.java b/src/test/test2/Inner.java
new file mode 100644
index 0000000..15e0866
--- /dev/null
+++ b/src/test/test2/Inner.java
@@ -0,0 +1,14 @@
+package test2;
+
+@SuppressWarnings("unused")
+public class Inner {
+ public void sample() throws Exception {
+ java.util.Properties props = new java.util.Properties();
+ java.rmi.activation.ActivationGroupDesc.CommandEnvironment ace = null;
+ java.rmi.activation.ActivationGroupDesc agd = new
+ java.rmi.activation.ActivationGroupDesc(props,ace);
+ }
+ public static void main(String args[]) {
+ System.out.println("Inner");
+ }
+}
diff --git a/src/test/test2/InsertAt.class b/src/test/test2/InsertAt.class
new file mode 100644
index 0000000..b8194ba
--- /dev/null
+++ b/src/test/test2/InsertAt.class
Binary files differ
diff --git a/src/test/test2/InsertAt.java b/src/test/test2/InsertAt.java
new file mode 100644
index 0000000..196028e
--- /dev/null
+++ b/src/test/test2/InsertAt.java
@@ -0,0 +1,23 @@
+package test2;
+
+public class InsertAt {
+ public int counter = 0;
+
+ public int foo() {
+ for (int i = 0; i < 3; i++)
+ counter++;
+
+ return counter;
+ }
+
+ public int bar() {
+ return bar2(7);
+ }
+
+ public int bar2(int k) {
+ int i = 1;
+ int j = i + 3;
+ k += i + j;
+ return k;
+ }
+}
diff --git a/src/test/test2/InsertLocal.class b/src/test/test2/InsertLocal.class
new file mode 100644
index 0000000..d0f32f2
--- /dev/null
+++ b/src/test/test2/InsertLocal.class
Binary files differ
diff --git a/src/test/test2/InsertLocal.java b/src/test/test2/InsertLocal.java
new file mode 100644
index 0000000..43794cc
--- /dev/null
+++ b/src/test/test2/InsertLocal.java
@@ -0,0 +1,33 @@
+package test2;
+
+public class InsertLocal {
+ public int run() {
+ return (int)(foo(4, 3.14, "pai") * 100) + field;
+ }
+
+ private int field = 0;
+
+ public double foo(int i, double d, String s) {
+ int k;
+
+ for (k = 0; k < i; k++)
+ d++;
+
+ return d;
+ }
+
+ public int run2() {
+ String s = ".";
+ int k = 0;
+ return k + s.length();
+ }
+
+ @SuppressWarnings("unused")
+ public int run3() {
+ int i = 0;
+ int j = field;
+ int k = run2();
+ InsertLocal obj = new InsertLocal();
+ return i;
+ }
+}
diff --git a/src/test/test2/LocalVar.class b/src/test/test2/LocalVar.class
new file mode 100644
index 0000000..a1d9357
--- /dev/null
+++ b/src/test/test2/LocalVar.class
Binary files differ
diff --git a/src/test/test2/LocalVar.java b/src/test/test2/LocalVar.java
new file mode 100644
index 0000000..0ba740b
--- /dev/null
+++ b/src/test/test2/LocalVar.java
@@ -0,0 +1,6 @@
+package test2;
+
+public class LocalVar {
+ public String toString() { return "123"; }
+ public int foo() { return toString().length(); }
+}
diff --git a/src/test/test2/MakeStaticMethod.class b/src/test/test2/MakeStaticMethod.class
new file mode 100644
index 0000000..320f0f3
--- /dev/null
+++ b/src/test/test2/MakeStaticMethod.class
Binary files differ
diff --git a/src/test/test2/MethodCall.class b/src/test/test2/MethodCall.class
new file mode 100644
index 0000000..6b8fc15
--- /dev/null
+++ b/src/test/test2/MethodCall.class
Binary files differ
diff --git a/src/test/test2/MethodCall.java b/src/test/test2/MethodCall.java
new file mode 100644
index 0000000..22341a0
--- /dev/null
+++ b/src/test/test2/MethodCall.java
@@ -0,0 +1,8 @@
+package test2;
+
+public class MethodCall {
+ public Object bar() {
+ String[] str = new String[] { "one", "two" };
+ return str.clone();
+ }
+}
diff --git a/src/test/test2/Nested$Inner3.class b/src/test/test2/Nested$Inner3.class
new file mode 100644
index 0000000..21afc9a
--- /dev/null
+++ b/src/test/test2/Nested$Inner3.class
Binary files differ
diff --git a/src/test/test2/Nested.class b/src/test/test2/Nested.class
new file mode 100644
index 0000000..529317a
--- /dev/null
+++ b/src/test/test2/Nested.class
Binary files differ
diff --git a/src/test/test2/Nested.java b/src/test/test2/Nested.java
new file mode 100644
index 0000000..6bb4547
--- /dev/null
+++ b/src/test/test2/Nested.java
@@ -0,0 +1,23 @@
+package test2;
+
+@SuppressWarnings("unused")
+public class Nested {
+ private int i = 3;
+ private int geti() { return i; }
+ private static int getj(int k) { return k; }
+ public void seti(int i) { this.i = i; }
+
+ public class Inner {
+ public int geti() { return i; }
+ public void foo(Inner2 in) {}
+ }
+
+ public class Inner2 {
+ public int geti() { return i; }
+ }
+
+ public static class Inner3 {
+ public int f() { return 1; }
+ public static int g() { return 1; }
+ }
+}
diff --git a/src/test/test2/Nested2$Inner.class b/src/test/test2/Nested2$Inner.class
new file mode 100644
index 0000000..0267e5e
--- /dev/null
+++ b/src/test/test2/Nested2$Inner.class
Binary files differ
diff --git a/src/test/test2/Nested2.class b/src/test/test2/Nested2.class
new file mode 100644
index 0000000..7cb3b34
--- /dev/null
+++ b/src/test/test2/Nested2.class
Binary files differ
diff --git a/src/test/test2/Nested2.java b/src/test/test2/Nested2.java
new file mode 100644
index 0000000..728c1b7
--- /dev/null
+++ b/src/test/test2/Nested2.java
@@ -0,0 +1,16 @@
+package test2;
+
+@SuppressWarnings("unused")
+public class Nested2 {
+ private int i = 3;
+ private double d = 3.0;
+ private String s = "OK";
+ private int geti() { return i; }
+ private static int getj(int k) { return k; }
+ public void seti(int i) { this.i = i; }
+
+ public static class Inner {
+ public int f() { return 1; }
+ public static int g() { return 1; }
+ }
+}
diff --git a/src/test/test2/Nested3$Inner.class b/src/test/test2/Nested3$Inner.class
new file mode 100644
index 0000000..dbc70b6
--- /dev/null
+++ b/src/test/test2/Nested3$Inner.class
Binary files differ
diff --git a/src/test/test2/Nested3.class b/src/test/test2/Nested3.class
new file mode 100644
index 0000000..a1e2a73
--- /dev/null
+++ b/src/test/test2/Nested3.class
Binary files differ
diff --git a/src/test/test2/Nested3.java b/src/test/test2/Nested3.java
new file mode 100644
index 0000000..9e9f1ab
--- /dev/null
+++ b/src/test/test2/Nested3.java
@@ -0,0 +1,17 @@
+package test2;
+
+@SuppressWarnings("unused")
+public class Nested3 {
+ private int i = 0;
+ private int geti() { return i; }
+
+ Nested3(int j) { i = 1; }
+
+ private Nested3() { i = 2; }
+
+ private Nested3(String s) { i = 3; }
+
+ public static class Inner {
+ public int g() { return 1; }
+ }
+}
diff --git a/src/test/test2/Nested4$Inner.class b/src/test/test2/Nested4$Inner.class
new file mode 100644
index 0000000..b770ef7
--- /dev/null
+++ b/src/test/test2/Nested4$Inner.class
Binary files differ
diff --git a/src/test/test2/Nested4.class b/src/test/test2/Nested4.class
new file mode 100644
index 0000000..550fb40
--- /dev/null
+++ b/src/test/test2/Nested4.class
Binary files differ
diff --git a/src/test/test2/Nested4.java b/src/test/test2/Nested4.java
new file mode 100644
index 0000000..04bcd76
--- /dev/null
+++ b/src/test/test2/Nested4.java
@@ -0,0 +1,6 @@
+package test2;
+
+@SuppressWarnings("unused")
+public class Nested4 {
+ private static int value = 6;
+}
diff --git a/src/test/test2/NewArray.class b/src/test/test2/NewArray.class
new file mode 100644
index 0000000..9dfbbe6
--- /dev/null
+++ b/src/test/test2/NewArray.class
Binary files differ
diff --git a/src/test/test2/NewArray.java b/src/test/test2/NewArray.java
new file mode 100644
index 0000000..f7c0439
--- /dev/null
+++ b/src/test/test2/NewArray.java
@@ -0,0 +1,19 @@
+package test2;
+
+@SuppressWarnings("unused")
+public class NewArray {
+ public int run() {
+ return foo(1);
+ }
+
+ public int foo(int i) {
+ String[] s1 = new String[3];
+ String[][] s2 = new String[4][];
+ String[][] s3 = new String[5][6];
+ int[] i1 = new int[7];
+ int[][] i2 = new int[8][];
+ int[][] i3 = new int[9][10];
+ int[][][] i4 = new int[11][12][];
+ return i;
+ }
+}
diff --git a/src/test/test2/NewExprInTry.class b/src/test/test2/NewExprInTry.class
new file mode 100644
index 0000000..79ca894
--- /dev/null
+++ b/src/test/test2/NewExprInTry.class
Binary files differ
diff --git a/src/test/test2/NewExprInTry.java b/src/test/test2/NewExprInTry.java
new file mode 100644
index 0000000..70dc9b2
--- /dev/null
+++ b/src/test/test2/NewExprInTry.java
@@ -0,0 +1,29 @@
+package test2;
+
+import java.util.HashMap;
+
+@SuppressWarnings("rawtypes")
+class HashMapWrapper extends HashMap {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ HashMapWrapper(int size, int args) {
+ super(size);
+ }
+}
+
+@SuppressWarnings({"rawtypes","unused"})
+public class NewExprInTry {
+ public int run() {
+ return foo(6);
+ }
+
+ public int foo(int size) {
+ HashMap h;
+ try {
+ h = new HashMap(size);
+ }
+ catch (Exception e) {}
+ return 1;
+ }
+}
diff --git a/src/test/test2/NewExprTry.class b/src/test/test2/NewExprTry.class
new file mode 100644
index 0000000..f70141e
--- /dev/null
+++ b/src/test/test2/NewExprTry.class
Binary files differ
diff --git a/src/test/test2/NewExprTry.java b/src/test/test2/NewExprTry.java
new file mode 100644
index 0000000..f728dbe
--- /dev/null
+++ b/src/test/test2/NewExprTry.java
@@ -0,0 +1,21 @@
+package test2;
+
+public class NewExprTry {
+ public NewExprTry() { this(3); }
+
+ public NewExprTry(int i) {}
+
+ public int run() {
+ return foo(3).getClass().getName().length();
+ }
+
+ public Object foo(int i) {
+ NewExprTry obj = new NewExprTry(i);
+ return obj;
+ }
+
+ public static void main(String[] args) {
+ @SuppressWarnings("unused")
+ NewExprTry obj = new NewExprTry(3);
+ }
+}
diff --git a/src/test/test2/NewOp.class b/src/test/test2/NewOp.class
new file mode 100644
index 0000000..da62b7c
--- /dev/null
+++ b/src/test/test2/NewOp.class
Binary files differ
diff --git a/src/test/test2/NewOp.java b/src/test/test2/NewOp.java
new file mode 100644
index 0000000..a933425
--- /dev/null
+++ b/src/test/test2/NewOp.java
@@ -0,0 +1,16 @@
+package test2;
+
+class NewOp2 {
+ NewOp2(long j, NewOp op, Object obj, long k) {}
+}
+
+public class NewOp {
+ @SuppressWarnings("rawtypes")
+ java.util.Vector listenerList;
+ static int i = 0;
+ static String s;
+ static {
+ String m = "test";
+ s = m.substring(1);
+ }
+}
diff --git a/src/test/test2/NullArgTest.class b/src/test/test2/NullArgTest.class
new file mode 100644
index 0000000..79dd719
--- /dev/null
+++ b/src/test/test2/NullArgTest.class
Binary files differ
diff --git a/src/test/test2/PrivateMethod.java b/src/test/test2/PrivateMethod.java
new file mode 100644
index 0000000..51ed75f
--- /dev/null
+++ b/src/test/test2/PrivateMethod.java
@@ -0,0 +1,11 @@
+package test2;
+
+class PrivateMethod2 {
+ private int f() { return 0; }
+
+ int g() { return f(); }
+}
+
+public class PrivateMethod {
+ public int i;
+}
diff --git a/src/test/test2/Prune.java b/src/test/test2/Prune.java
new file mode 100644
index 0000000..f98f776
--- /dev/null
+++ b/src/test/test2/Prune.java
@@ -0,0 +1,17 @@
+package test2;
+
+public class Prune extends java.awt.Point implements Cloneable, Runnable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+ public int value;
+
+ public Prune(int i) {
+ value = i;
+ }
+
+ public void run() {}
+
+ public int sum() {
+ return x + y;
+ }
+}
diff --git a/src/test/test2/Remove.class b/src/test/test2/Remove.class
new file mode 100644
index 0000000..d2bab30
--- /dev/null
+++ b/src/test/test2/Remove.class
Binary files differ
diff --git a/src/test/test2/Remove.java b/src/test/test2/Remove.java
new file mode 100644
index 0000000..3f980f6
--- /dev/null
+++ b/src/test/test2/Remove.java
@@ -0,0 +1,25 @@
+package test2;
+
+class RemoveParent {
+ int p;
+}
+
+public class Remove extends RemoveParent {
+ public int f1;
+ public int f2;
+ public String f3;
+ public Remove f4;
+ public int[] f5;
+ public int f6;
+ int g = 3;
+
+ public void bar() {}
+ public Remove() { g = 7; }
+ public Remove(int i) { g = i; }
+
+ public int foo() {
+ return g;
+ }
+
+ public void bar2() {}
+}
diff --git a/src/test/test2/RemoveCall.class b/src/test/test2/RemoveCall.class
new file mode 100644
index 0000000..1b81839
--- /dev/null
+++ b/src/test/test2/RemoveCall.class
Binary files differ
diff --git a/src/test/test2/RemoveCall.java b/src/test/test2/RemoveCall.java
new file mode 100644
index 0000000..909ef37
--- /dev/null
+++ b/src/test/test2/RemoveCall.java
@@ -0,0 +1,13 @@
+package test2;
+
+public class RemoveCall {
+ int k = 0;
+ public int bar() throws Exception {
+ foo(3);
+ return k;
+ }
+
+ public void foo(int k) throws Exception {
+ k = 1;
+ }
+}
diff --git a/src/test/test2/ReplaceClassName.class b/src/test/test2/ReplaceClassName.class
new file mode 100644
index 0000000..db3611b
--- /dev/null
+++ b/src/test/test2/ReplaceClassName.class
Binary files differ
diff --git a/src/test/test2/ReplaceClassName.java b/src/test/test2/ReplaceClassName.java
new file mode 100644
index 0000000..b41d9db
--- /dev/null
+++ b/src/test/test2/ReplaceClassName.java
@@ -0,0 +1,18 @@
+package test2;
+
+class ReplaceClassName2 {
+}
+
+class ReplaceClassName3 {
+}
+
+public class ReplaceClassName {
+ ReplaceClassName2 field;
+ public ReplaceClassName(ReplaceClassName2 a) {
+ field = a;
+ }
+
+ public void foo(ReplaceClassName2 a) {
+ field = a;
+ }
+}
diff --git a/src/test/test2/SetExceptions.class b/src/test/test2/SetExceptions.class
new file mode 100644
index 0000000..2331eb9
--- /dev/null
+++ b/src/test/test2/SetExceptions.class
Binary files differ
diff --git a/src/test/test2/SetExceptions.java b/src/test/test2/SetExceptions.java
new file mode 100644
index 0000000..fd3f232
--- /dev/null
+++ b/src/test/test2/SetExceptions.java
@@ -0,0 +1,7 @@
+package test2;
+
+public class SetExceptions {
+ public void f() throws Exception {
+ throw new Exception();
+ }
+}
diff --git a/src/test/test2/SetSuper.class b/src/test/test2/SetSuper.class
new file mode 100644
index 0000000..5586173
--- /dev/null
+++ b/src/test/test2/SetSuper.class
Binary files differ
diff --git a/src/test/test2/SetSuperIntf.class b/src/test/test2/SetSuperIntf.class
new file mode 100644
index 0000000..d559556
--- /dev/null
+++ b/src/test/test2/SetSuperIntf.class
Binary files differ
diff --git a/src/test/test2/SetSuperParent.class b/src/test/test2/SetSuperParent.class
new file mode 100644
index 0000000..2825d7d
--- /dev/null
+++ b/src/test/test2/SetSuperParent.class
Binary files differ
diff --git a/src/test/test2/StaticArraysMem.java b/src/test/test2/StaticArraysMem.java
new file mode 100644
index 0000000..5c220a0
--- /dev/null
+++ b/src/test/test2/StaticArraysMem.java
@@ -0,0 +1,5 @@
+package test2;
+
+public class StaticArraysMem {
+ int i;
+}
diff --git a/src/test/test2/StaticFinal.class b/src/test/test2/StaticFinal.class
new file mode 100644
index 0000000..69be993
--- /dev/null
+++ b/src/test/test2/StaticFinal.class
Binary files differ
diff --git a/src/test/test2/StaticMember.class b/src/test/test2/StaticMember.class
new file mode 100644
index 0000000..10a7209
--- /dev/null
+++ b/src/test/test2/StaticMember.class
Binary files differ
diff --git a/src/test/test2/StaticMember.java b/src/test/test2/StaticMember.java
new file mode 100644
index 0000000..10c14bd
--- /dev/null
+++ b/src/test/test2/StaticMember.java
@@ -0,0 +1,11 @@
+package test2;
+
+interface IStaticMember {
+ int bar();
+}
+
+public class StaticMember implements IStaticMember {
+ public static int k = 3;
+ public static int foo() { return 7; }
+ public int bar() { return 3; }
+}
diff --git a/src/test/test2/StaticMember2.class b/src/test/test2/StaticMember2.class
new file mode 100644
index 0000000..fe46c67
--- /dev/null
+++ b/src/test/test2/StaticMember2.class
Binary files differ
diff --git a/src/test/test2/StaticMember2.java b/src/test/test2/StaticMember2.java
new file mode 100644
index 0000000..747bb9b
--- /dev/null
+++ b/src/test/test2/StaticMember2.java
@@ -0,0 +1,12 @@
+package test2;
+
+public class StaticMember2 {
+ public static final int f = 11;
+ public static final long fj = 13L;
+ public static final boolean fb = false;
+ public static final double fd = 23.7;
+
+ public static int k = 3;
+ public static int seven() { return 7; }
+ public int bar() { return 3; }
+}
diff --git a/src/test/test2/SuperCall.class b/src/test/test2/SuperCall.class
new file mode 100644
index 0000000..b7294c2
--- /dev/null
+++ b/src/test/test2/SuperCall.class
Binary files differ
diff --git a/src/test/test2/SuperCall.java b/src/test/test2/SuperCall.java
new file mode 100644
index 0000000..f1c4202
--- /dev/null
+++ b/src/test/test2/SuperCall.java
@@ -0,0 +1,20 @@
+package test2;
+
+class SuperClass {
+ public void foo() throws Exception {}
+}
+
+public class SuperCall extends SuperClass {
+ int i = 0;
+ public int bar() throws Exception {
+ foo();
+ return 1;
+ }
+
+ public void foo() throws Exception {
+ if (++i > 5)
+ throw new Exception("infinite regression?");
+
+ super.foo();
+ }
+}
diff --git a/src/test/test2/SuperInterface1.java b/src/test/test2/SuperInterface1.java
new file mode 100644
index 0000000..4bb6c49
--- /dev/null
+++ b/src/test/test2/SuperInterface1.java
@@ -0,0 +1,6 @@
+package test2;
+
+public interface SuperInterface1 {
+ public int getAge();
+ public int setAge(int value);
+}
diff --git a/src/test/test2/SuperInterface2.java b/src/test/test2/SuperInterface2.java
new file mode 100644
index 0000000..27d5925
--- /dev/null
+++ b/src/test/test2/SuperInterface2.java
@@ -0,0 +1,3 @@
+package test2;
+
+public interface SuperInterface2 extends SuperInterface1 {}
diff --git a/src/test/test2/SuperInterface3.class b/src/test/test2/SuperInterface3.class
new file mode 100644
index 0000000..4b6c7b7
--- /dev/null
+++ b/src/test/test2/SuperInterface3.class
Binary files differ
diff --git a/src/test/test2/Switch.class b/src/test/test2/Switch.class
new file mode 100644
index 0000000..78e8ea9
--- /dev/null
+++ b/src/test/test2/Switch.class
Binary files differ
diff --git a/src/test/test2/Synch.class b/src/test/test2/Synch.class
new file mode 100644
index 0000000..c5a5ddd
--- /dev/null
+++ b/src/test/test2/Synch.class
Binary files differ
diff --git a/src/test/test2/Where.java b/src/test/test2/Where.java
new file mode 100644
index 0000000..29907f8
--- /dev/null
+++ b/src/test/test2/Where.java
@@ -0,0 +1,10 @@
+package test2;
+
+public class Where {
+ static int i = 0;
+ static String s;
+ static {
+ String m = "test";
+ s = m.substring(1);
+ }
+}
diff --git a/src/test/test3/Anno.java b/src/test/test3/Anno.java
new file mode 100644
index 0000000..675b29d
--- /dev/null
+++ b/src/test/test3/Anno.java
@@ -0,0 +1,15 @@
+package test3;
+
+public @interface Anno {
+ char c() default '0';
+ boolean bool() default true;
+ byte b() default 1;
+ short s() default 2;
+ int i() default 3;
+ long j() default 4L;
+ float f() default 5.0F;
+ double d() default 6.0;
+ String str() default "7";
+ Class clazz() default AnnoTest.class;
+ Anno2 anno2() default @Anno2();
+}
diff --git a/src/test/test3/Anno2.java b/src/test/test3/Anno2.java
new file mode 100644
index 0000000..627c053
--- /dev/null
+++ b/src/test/test3/Anno2.java
@@ -0,0 +1,11 @@
+package test3;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Anno2 {
+ int[] i() default { 1, 2, 3 };
+ String[] str() default { "a", "b", "c" };
+ ColorName color() default ColorName.RED;
+ ColorName[] color2() default { ColorName.BLUE };
+}
diff --git a/src/test/test3/Anno3.java b/src/test/test3/Anno3.java
new file mode 100644
index 0000000..28d9833
--- /dev/null
+++ b/src/test/test3/Anno3.java
@@ -0,0 +1,17 @@
+package test3;
+
+import java.lang.annotation.*;
+
+public @interface Anno3 {
+ char[] c() default {'0'};
+ boolean[] bool() default {true};
+ byte[] b() default {1};
+ short[] s() default {2};
+ int[] i() default {3};
+ long[] j() default {4L};
+ float[] f() default {5.0F};
+ double[] d() default {6.0};
+ String[] str() default {"7"};
+ Class[] clazz() default {AnnoTest.class};
+ Anno2[] anno2() default {@Anno2(i=11)};
+}
diff --git a/src/test/test3/Anno6.java b/src/test/test3/Anno6.java
new file mode 100644
index 0000000..deee6f7
--- /dev/null
+++ b/src/test/test3/Anno6.java
@@ -0,0 +1,6 @@
+package test3;
+
+public @interface Anno6 {
+ String[] str1() default {};
+ String[] str2();
+}
diff --git a/src/test/test3/AnnoTest.java b/src/test/test3/AnnoTest.java
new file mode 100644
index 0000000..072a0ce
--- /dev/null
+++ b/src/test/test3/AnnoTest.java
@@ -0,0 +1,21 @@
+package test3;
+
+@Anno()
+public class AnnoTest {}
+
+@Anno(c='a', bool=false, b=11, s=12, i=13, j=14L, f=15.0F, d=16.0,
+ str="17", clazz=String.class, anno2=@Anno2(i={11, 12, 13}))
+class AnnoTest2 {}
+
+@Anno() @Anno2(i={1})
+class AnnoTest3 {}
+
+@Anno() @Anno2() @Anno3()
+class AnnoTest4 {}
+
+class AnnoTest5 {
+ @Anno()
+ void foo() {}
+
+ @Anno2() int bar;
+} \ No newline at end of file
diff --git a/src/test/test3/AnnoTest6.java b/src/test/test3/AnnoTest6.java
new file mode 100644
index 0000000..5c3e87f
--- /dev/null
+++ b/src/test/test3/AnnoTest6.java
@@ -0,0 +1,6 @@
+package test3;
+
+@Anno6(str2={})
+public class AnnoTest6 {
+ int foo() { return 1; }
+}
diff --git a/src/test/test3/CheckModify.java b/src/test/test3/CheckModify.java
new file mode 100644
index 0000000..cf35cb2
--- /dev/null
+++ b/src/test/test3/CheckModify.java
@@ -0,0 +1,5 @@
+package test3;
+
+public class CheckModify {
+ int i;
+}
diff --git a/src/test/test3/ChibaAnnotation.java b/src/test/test3/ChibaAnnotation.java
new file mode 100644
index 0000000..4e91714
--- /dev/null
+++ b/src/test/test3/ChibaAnnotation.java
@@ -0,0 +1,11 @@
+package test3;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ChibaAnnotation {
+ String name();
+ String version();
+ String description();
+ String interfaceName();
+}
diff --git a/src/test/test3/ColorName.java b/src/test/test3/ColorName.java
new file mode 100644
index 0000000..17ef480
--- /dev/null
+++ b/src/test/test3/ColorName.java
@@ -0,0 +1,5 @@
+package test3;
+
+public enum ColorName {
+ RED, GREEN, BLUE
+}
diff --git a/src/test/test3/Constructor.java b/src/test/test3/Constructor.java
new file mode 100644
index 0000000..2b6e1f4
--- /dev/null
+++ b/src/test/test3/Constructor.java
@@ -0,0 +1,37 @@
+package test3;
+
+class SuperConsturctor {
+ SuperConsturctor() {}
+ SuperConsturctor(int p, int q) {}
+ SuperConsturctor(int p, double r, long[] s, String t) {}
+}
+
+public class Constructor extends SuperConsturctor {
+ static String str = "ok?";
+ int i;
+
+ public Constructor() {
+ this(3);
+ ++i;
+ }
+
+ public Constructor(int k) {
+ super(0, 1.0, null, "test");
+ i += k;
+ }
+
+ public Constructor(String s) {
+ this();
+ i += 10;
+ }
+
+ public Constructor(double d) {
+ super(1, 2);
+ i += 100;
+ }
+
+ public int run() {
+ str = null;
+ return 0;
+ }
+}
diff --git a/src/test/test3/Constructor2.java b/src/test/test3/Constructor2.java
new file mode 100644
index 0000000..bfc7cf2
--- /dev/null
+++ b/src/test/test3/Constructor2.java
@@ -0,0 +1,7 @@
+package test3;
+
+public class Constructor2 extends SuperConsturctor {
+ static String str = null;
+ int i = 0;
+ public Constructor2() {}
+}
diff --git a/src/test/test3/CopyAnno.java b/src/test/test3/CopyAnno.java
new file mode 100644
index 0000000..cc4ac33
--- /dev/null
+++ b/src/test/test3/CopyAnno.java
@@ -0,0 +1,10 @@
+package test3;
+
+class CopyAnnoBase {
+ int x = 3;
+ @VisibleAnno public int getX() { return x; }
+}
+
+public class CopyAnno extends CopyAnnoBase {
+ public int getX() { return super.getX(); }
+}
diff --git a/src/test/test3/DefineClassCapability.java b/src/test/test3/DefineClassCapability.java
new file mode 100644
index 0000000..60b9183
--- /dev/null
+++ b/src/test/test3/DefineClassCapability.java
@@ -0,0 +1,7 @@
+/*
+ * This is used as a capability for running CtClass#toClass().
+ */
+package test3;
+
+public class DefineClassCapability {
+}
diff --git a/src/test/test3/EmptyCatch.java b/src/test/test3/EmptyCatch.java
new file mode 100644
index 0000000..bd1122e
--- /dev/null
+++ b/src/test/test3/EmptyCatch.java
@@ -0,0 +1,14 @@
+package test3;
+
+public class EmptyCatch {
+ public int test(int i) {
+ try {
+ i += 200;
+ }
+ finally {
+ i += 10;
+ }
+
+ return i;
+ }
+}
diff --git a/src/test/test3/EmptyConstructor.java b/src/test/test3/EmptyConstructor.java
new file mode 100644
index 0000000..fe53dc3
--- /dev/null
+++ b/src/test/test3/EmptyConstructor.java
@@ -0,0 +1,33 @@
+package test3;
+
+public class EmptyConstructor {
+ static {}
+ public int value;
+}
+
+class EmptyConstructor2 extends EmptyConstructor {
+ static {}
+ public int value2;
+}
+
+class EmptyConstructor3 extends EmptyConstructor {
+ public int value3;
+ public EmptyConstructor3() {}
+ public EmptyConstructor3(int x) { super(); }
+}
+
+class EmptyConstructor4 extends EmptyConstructor3 {
+ public static int sv = 3;
+ public int value3;
+ EmptyConstructor4(int x) {
+ super(x);
+ }
+
+ EmptyConstructor4(double x) {
+ this();
+ }
+
+ EmptyConstructor4() {
+ value3 = 7;
+ }
+}
diff --git a/src/test/test3/Enhancer.java b/src/test/test3/Enhancer.java
new file mode 100644
index 0000000..e3a93e8
--- /dev/null
+++ b/src/test/test3/Enhancer.java
@@ -0,0 +1,181 @@
+package test3;
+
+import javassist.*;
+import java.lang.reflect.Method;
+import java.lang.reflect.Field;
+
+/* Test code
+ */
+class EnhanceTest {
+ public EnhanceTest() { super(); }
+ public void foo(String s) { System.out.println(s); }
+}
+
+@SuppressWarnings({"rawtypes","unchecked","unused"})
+public class Enhancer {
+ private ClassPool pool;
+ private CtClass superClass;
+ private CtClass thisClass;
+ private Class thisJavaClass;
+ private Interceptor interceptor;
+ private int unique;
+
+ private static final String INTERCEPTOR = "interceptor";
+
+ /* Test method
+ */
+ public static void main(String[] args) throws Exception {
+ Enhancer e = new Enhancer(test3.EnhanceTest.class);
+ e.overrideAll();
+ e.setCallback(new Interceptor() {
+ public Object invoke(Object self, Method m, Object[] args)
+ throws Exception
+ {
+ System.out.println("intercept: " + m);
+ return m.invoke(self, args);
+ }
+ });
+ Class c = e.createClass();
+ EnhanceTest obj = (EnhanceTest)c.getConstructor().newInstance();
+ obj.foo("test");
+ }
+
+ public static interface Interceptor {
+ Object invoke(Object self, Method m, Object[] args) throws Exception;
+ }
+
+ public Enhancer(Class clazz)
+ throws CannotCompileException, NotFoundException
+ {
+ this(makeClassPool(clazz).get(clazz.getName()));
+ }
+
+ private static ClassPool makeClassPool(Class clazz) {
+ ClassPool cp = new ClassPool();
+ cp.appendSystemPath();
+ cp.insertClassPath(new ClassClassPath(clazz));
+ return cp;
+ }
+
+ public Enhancer(CtClass superClass)
+ throws CannotCompileException, NotFoundException
+ {
+ this.pool = superClass.getClassPool();
+ this.superClass = superClass;
+ String name = superClass.getName() + "_proxy";
+ thisClass = pool.makeClass(name);
+ thisClass.setSuperclass(superClass);
+ String src =
+ "public static " + this.getClass().getName()
+ + ".Interceptor " + INTERCEPTOR + ";";
+
+ thisClass.addField(CtField.make(src, thisClass));
+ this.thisJavaClass = null;
+ unique = 0;
+ }
+
+ public void overrideAll()
+ throws CannotCompileException, NotFoundException
+ {
+ CtMethod[] methods = superClass.getMethods();
+ String delegatorNamePrefix = thisClass.makeUniqueName("d");
+ for (int i = 0; i < methods.length; i++) {
+ CtMethod m = methods[i];
+ int mod = m.getModifiers();
+ if (!Modifier.isFinal(mod) && !Modifier.isAbstract(mod)
+ && !Modifier.isStatic(mod))
+ override(m, delegatorNamePrefix + i);
+ }
+ }
+
+ public void override(CtMethod m, String delegatorName)
+ throws CannotCompileException, NotFoundException
+ {
+ String fieldName = "m" + unique++;
+ thisClass.addField(
+ CtField.make("private java.lang.reflect.Method "
+ + fieldName + ";", thisClass));
+ CtMethod delegator = CtNewMethod.delegator(m, thisClass);
+ delegator.setModifiers(Modifier.clear(delegator.getModifiers(),
+ Modifier.NATIVE));
+ delegator.setName(delegatorName);
+ thisClass.addMethod(delegator);
+ thisClass.addMethod(makeMethod(m, fieldName, delegatorName));
+ }
+
+ private CtMethod makeMethod(CtMethod m, String fieldName,
+ String delegatorName)
+ throws CannotCompileException, NotFoundException
+ {
+ String factory = this.getClass().getName() + ".findMethod(this, \"" +
+ delegatorName + "\");";
+ String body
+ = "{ if (" + fieldName + " == null) " +
+ fieldName + " = " + factory +
+ "return ($r)" + INTERCEPTOR + ".invoke(this, " + fieldName +
+ ", $args); }";
+ CtMethod m2 = CtNewMethod.make(m.getReturnType(),
+ m.getName(),
+ m.getParameterTypes(),
+ m.getExceptionTypes(),
+ body, thisClass);
+ m2.setModifiers(Modifier.clear(m.getModifiers(),
+ Modifier.NATIVE));
+ return m2;
+ }
+
+ /* A runtime support routine called by an enhanced object.
+ */
+ public static Method findMethod(Object self, String name) {
+ Method[] methods = self.getClass().getMethods();
+ int n = methods.length;
+ for (int i = 0; i < n; i++)
+ if (methods[i].getName().equals(name))
+ return methods[i];
+
+ throw new RuntimeException("not found " + name
+ + " in " + self.getClass());
+ }
+
+ public Class createClass() {
+ if (thisJavaClass == null)
+ try {
+ thisClass.debugWriteFile();
+ thisJavaClass = thisClass.toClass();
+ setInterceptor();
+ }
+ catch (CannotCompileException e) {
+ throw new RuntimeException(e);
+ }
+
+ return thisJavaClass;
+ }
+
+ private static void writeFile(CtClass cc) {
+ try {
+ cc.stopPruning(true);
+ cc.writeFile();
+ cc.defrost();
+ cc.stopPruning(false);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void setCallback(Interceptor mi) {
+ interceptor = mi;
+ setInterceptor();
+ }
+
+ private void setInterceptor() {
+ if (thisJavaClass != null && interceptor != null)
+ try {
+ Field f = thisJavaClass.getField(INTERCEPTOR);
+ f.set(null, interceptor);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/test/test3/Erasure.java b/src/test/test3/Erasure.java
new file mode 100644
index 0000000..7514981
--- /dev/null
+++ b/src/test/test3/Erasure.java
@@ -0,0 +1,16 @@
+package test3;
+
+interface ErasureGet<T> {
+ T get();
+}
+
+public class Erasure<T> {
+ T value;
+ public Erasure(T t) { value = t; }
+ public Erasure() { value = null; }
+ public int run() {
+ @SuppressWarnings("unchecked")
+ ErasureGet<String> obj = (ErasureGet<String>)new Erasure<String>("1234");
+ return obj.get().length();
+ }
+}
diff --git a/src/test/test3/FieldAccessType.java b/src/test/test3/FieldAccessType.java
new file mode 100644
index 0000000..bb102ba
--- /dev/null
+++ b/src/test/test3/FieldAccessType.java
@@ -0,0 +1,11 @@
+package test3;
+
+public class FieldAccessType {
+ private int[] k;
+ public void access() {
+ k = new int[1];
+ @SuppressWarnings("unused")
+ int i = 3;
+ i += k[0];
+ }
+}
diff --git a/src/test/test3/FieldCopy.java b/src/test/test3/FieldCopy.java
new file mode 100644
index 0000000..5ac52a0
--- /dev/null
+++ b/src/test/test3/FieldCopy.java
@@ -0,0 +1,12 @@
+package test3;
+
+class FieldCopy2 {
+ int bar;
+}
+
+public class FieldCopy {
+ public @interface Test {
+ }
+
+ @Test private static int foo;
+}
diff --git a/src/test/test3/Frozen.java b/src/test/test3/Frozen.java
new file mode 100644
index 0000000..19ebdf4
--- /dev/null
+++ b/src/test/test3/Frozen.java
@@ -0,0 +1,7 @@
+package test3;
+
+public class Frozen {
+ int value;
+ public Frozen() { value = 3; }
+ public int get() { return value; }
+}
diff --git a/src/test/test3/GetMethods.java b/src/test/test3/GetMethods.java
new file mode 100644
index 0000000..64e66bd
--- /dev/null
+++ b/src/test/test3/GetMethods.java
@@ -0,0 +1,32 @@
+package test3;
+
+@SuppressWarnings("unused")
+class SuperGetMethods {
+ public int f0;
+ protected double d0;
+ String s0;
+ private char c0;
+
+ public void mpub0() {}
+ protected void mpro0() {}
+ void mpack0() {}
+ private void mpri0() {}
+}
+
+@SuppressWarnings("unused")
+public class GetMethods extends SuperGetMethods {
+ public GetMethods(int i) {}
+ protected GetMethods(String i, int j) {}
+ GetMethods() {}
+ private GetMethods(int i, int j) {}
+
+ public int f;
+ protected double d;
+ String s;
+ private char c;
+
+ public void mpub() {}
+ protected void mpro() {}
+ void mpack() {}
+ private void mpri() {}
+}
diff --git a/src/test/test3/InheritCons.java b/src/test/test3/InheritCons.java
new file mode 100644
index 0000000..d37719b
--- /dev/null
+++ b/src/test/test3/InheritCons.java
@@ -0,0 +1,9 @@
+package test3;
+
+public class InheritCons {
+ int value;
+ public InheritCons() { this(2); }
+ private InheritCons(int i) { value = i; }
+ protected InheritCons(String s) { this(0); }
+ InheritCons(char c) { this(1); }
+}
diff --git a/src/test/test3/InnerClass.java b/src/test/test3/InnerClass.java
new file mode 100644
index 0000000..411e60b
--- /dev/null
+++ b/src/test/test3/InnerClass.java
@@ -0,0 +1,7 @@
+package test3;
+
+public class InnerClass {
+ int y;
+ public static class Inner { int x; }
+ public class Inner2 { int x; }
+}
diff --git a/src/test/test3/InnerMethod.java b/src/test/test3/InnerMethod.java
new file mode 100644
index 0000000..d00aa2b
--- /dev/null
+++ b/src/test/test3/InnerMethod.java
@@ -0,0 +1,17 @@
+package test3;
+
+public class InnerMethod {
+ static int f = 0;
+ static int counter = 3;
+ private static void test() {}
+ static Inner inner = new Inner();
+
+ static class Inner {
+ protected static void test() { f = 1; }
+ }
+
+ public int foo() {
+ test();
+ return f;
+ }
+}
diff --git a/src/test/test3/InsParam.java b/src/test/test3/InsParam.java
new file mode 100644
index 0000000..6895092
--- /dev/null
+++ b/src/test/test3/InsParam.java
@@ -0,0 +1,16 @@
+package test3;
+
+public class InsParam {
+ public int bar(int k) { return k; }
+
+ public int bar2(int k) { return k; }
+
+ public int foo(int i) {
+ int k = bar2(i);
+ return k;
+ }
+
+ public int poi(int i, String s) {
+ return i + s.length();
+ }
+}
diff --git a/src/test/test3/InsertAfter.java b/src/test/test3/InsertAfter.java
new file mode 100644
index 0000000..731a4bc
--- /dev/null
+++ b/src/test/test3/InsertAfter.java
@@ -0,0 +1,17 @@
+package test3;
+
+public class InsertAfter {
+ int k;
+
+ public InsertAfter() {
+ k = 3;
+ }
+
+ public int test() {
+ foo();
+ return k;
+ }
+ public void foo() {
+ ++k;
+ }
+}
diff --git a/src/test/test3/InsertBeforeType.java b/src/test/test3/InsertBeforeType.java
new file mode 100644
index 0000000..b61db31
--- /dev/null
+++ b/src/test/test3/InsertBeforeType.java
@@ -0,0 +1,11 @@
+package test3;
+
+public class InsertBeforeType {
+ String value = "";
+ void foo() { value += ":"; }
+ public int test() {
+ foo();
+ System.out.println(value);
+ return value.length();
+ }
+}
diff --git a/src/test/test3/InvokeArray.java b/src/test/test3/InvokeArray.java
new file mode 100644
index 0000000..fc2eb30
--- /dev/null
+++ b/src/test/test3/InvokeArray.java
@@ -0,0 +1,15 @@
+package test3;
+
+class InvokeArrayEle {
+ String name;
+}
+
+public class InvokeArray {
+ public int test() {
+ return doit(new InvokeArrayEle[3]);
+ }
+ public int doit(InvokeArrayEle[] ae) {
+ Object[] ae2 = (Object[])ae.clone();
+ return ae2.length;
+ }
+} \ No newline at end of file
diff --git a/src/test/test3/InvokeIntf.java b/src/test/test3/InvokeIntf.java
new file mode 100644
index 0000000..d1811ce
--- /dev/null
+++ b/src/test/test3/InvokeIntf.java
@@ -0,0 +1,30 @@
+package test3;
+
+interface InvokedIntf {
+ Object clone();
+}
+
+interface InvokedIntf2 extends InvokedIntf {
+}
+
+class InvokedIntf3 implements InvokedIntf2 {
+ public Object clone() {
+ try {
+ return super.clone();
+ }
+ catch (Exception e) {
+ return null;
+ }
+ }
+}
+
+public class InvokeIntf {
+ public int test() {
+ doit(new InvokedIntf3());
+ return 7;
+ }
+ public void doit(InvokedIntf2 ii) {
+ ii.clone();
+ ii.toString();
+ }
+}
diff --git a/src/test/test3/JIRA63.java b/src/test/test3/JIRA63.java
new file mode 100644
index 0000000..2ae84c6
--- /dev/null
+++ b/src/test/test3/JIRA63.java
@@ -0,0 +1,5 @@
+package test3;
+
+public class JIRA63 {
+
+}
diff --git a/src/test/test3/JIRA63Helper.java b/src/test/test3/JIRA63Helper.java
new file mode 100644
index 0000000..1c32c67
--- /dev/null
+++ b/src/test/test3/JIRA63Helper.java
@@ -0,0 +1,7 @@
+package test3;
+
+public class JIRA63Helper {
+ public static JIRA63Helper getAnObject(Object o) {
+ return new JIRA63Helper();
+ }
+}
diff --git a/src/test/test3/MethodRedirect.java b/src/test/test3/MethodRedirect.java
new file mode 100644
index 0000000..c7cacde
--- /dev/null
+++ b/src/test/test3/MethodRedirect.java
@@ -0,0 +1,21 @@
+package test3;
+
+interface MethodRedirectIntf {
+ int afo();
+}
+
+public class MethodRedirect implements MethodRedirectIntf {
+ @SuppressWarnings("unused")
+ private int foo() { return 0; }
+ public static int poi() { return 1; }
+ public int bar() { return 2; }
+ public int afo() { return 3; }
+
+ public int test() {
+ return bar();
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new MethodRedirect().test());
+ }
+}
diff --git a/src/test/test3/MethodRedirect2.java b/src/test/test3/MethodRedirect2.java
new file mode 100644
index 0000000..81c083c
--- /dev/null
+++ b/src/test/test3/MethodRedirect2.java
@@ -0,0 +1,43 @@
+package test3;
+
+interface MethodRedirect2SupIntf {
+ int foo();
+ int bar();
+ int bar2();
+}
+
+interface MethodRedirect2Intf extends MethodRedirect2SupIntf {
+ int bar2();
+}
+
+class MethodRedirect2SupSup {
+ public int bfo() { return 100; }
+ public int bfo2() { return 200; }
+}
+
+class MethodRedirect2Sup extends MethodRedirect2SupSup {
+ public int afo() { return 10; }
+ public int afo2() { return 20; }
+ public int bfo() { return 300; }
+}
+
+public class MethodRedirect2 extends MethodRedirect2Sup implements MethodRedirect2Intf {
+ public int foo() { return 1; }
+ public int bar() { return 2; }
+ public int bar2() { return 3; }
+
+ public int test(MethodRedirect2Intf intf, MethodRedirect2 clazz,
+ MethodRedirect2SupSup sup)
+ {
+ return intf.bar() + intf.bar2() + clazz.afo() + clazz.bfo() + sup.bfo();
+ }
+
+ public int test() {
+ MethodRedirect2 obj = new MethodRedirect2();
+ return test(obj, obj, obj);
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new MethodRedirect2().test());
+ }
+}
diff --git a/src/test/test3/Mods.java b/src/test/test3/Mods.java
new file mode 100644
index 0000000..3d4dbb1
--- /dev/null
+++ b/src/test/test3/Mods.java
@@ -0,0 +1,9 @@
+package test3;
+
+public class Mods {
+ public int foo;
+}
+
+interface Mods2 {
+ int foo();
+}
diff --git a/src/test/test3/Name.java b/src/test/test3/Name.java
new file mode 100644
index 0000000..7614e30
--- /dev/null
+++ b/src/test/test3/Name.java
@@ -0,0 +1,17 @@
+package test3;
+
+public class Name {
+ static int k;
+ static { k = 3; }
+
+ public Name() {}
+ public Name(int i) {}
+ public Name(Name n) {}
+ public Name(Name n, String s) {}
+
+ public void foo() {}
+ public void foo2(int i) {}
+ public void foo3(String s) {}
+ public void foo4(String[] s) {}
+ public void foo5(int i, String s) {}
+}
diff --git a/src/test/test3/NestedClass.java b/src/test/test3/NestedClass.java
new file mode 100644
index 0000000..e1459b2
--- /dev/null
+++ b/src/test/test3/NestedClass.java
@@ -0,0 +1,25 @@
+package test3;
+
+public class NestedClass {
+ public class Inner {
+ int i;
+ }
+
+ public static class StaticNested {
+ int k;
+ }
+
+ public Object foo() {
+ return new Object() {
+ public String toString() { return "OK"; }
+ };
+ }
+
+ public Object bar() {
+ class Local {
+ @SuppressWarnings("unused")
+ int j;
+ }
+ return new Local();
+ }
+}
diff --git a/src/test/test3/NewClass2.java b/src/test/test3/NewClass2.java
new file mode 100644
index 0000000..3df0f00
--- /dev/null
+++ b/src/test/test3/NewClass2.java
@@ -0,0 +1,5 @@
+package test3;
+
+@ChibaAnnotation(name="Chiba",version="Chiba",description="Chiba",interfaceName="Chiba")
+public class NewClass2 {
+} \ No newline at end of file
diff --git a/src/test/test3/NewExprTryCatch.java b/src/test/test3/NewExprTryCatch.java
new file mode 100644
index 0000000..f93b528
--- /dev/null
+++ b/src/test/test3/NewExprTryCatch.java
@@ -0,0 +1,30 @@
+package test3;
+
+class NewExprTryCatch2 {
+}
+
+public class NewExprTryCatch {
+ public void instrumentMe() {
+ // we need a 'new' expression to instrument, the exact type is not important
+ new Object();
+ // if the try/catch block below is removed, the error does not occur
+ try {
+ System.out.println();
+ } catch (Throwable t) {
+ }
+ }
+
+ public void me2() throws Exception {
+ // the error is somehow related to the string concatenation and local variables,
+ // when the code below is replaced with something else, the error does not occur.
+ String s1 = "a";
+ @SuppressWarnings("unused")
+ String s2 = s1 + "b";
+ }
+
+ public int test() throws Exception {
+ instrumentMe();
+ me2();
+ return 0;
+ }
+}
diff --git a/src/test/test3/PackName.java b/src/test/test3/PackName.java
new file mode 100644
index 0000000..111d3ed
--- /dev/null
+++ b/src/test/test3/PackName.java
@@ -0,0 +1,12 @@
+package test3;
+
+class PackName2 {
+ public static int k = 1;
+}
+public class PackName {
+ public static int test3 = 3;
+ public static int sub = 4;
+ public static int PackName2;
+
+ public static int get() { return test3; }
+}
diff --git a/src/test/test3/ParamAnno.java b/src/test/test3/ParamAnno.java
new file mode 100644
index 0000000..8277d9b
--- /dev/null
+++ b/src/test/test3/ParamAnno.java
@@ -0,0 +1,9 @@
+package test3;
+
+public class ParamAnno {
+ public int foo(int i, String s, ParamAnno pa, double d) {
+ return i + 1;
+ }
+
+ public void bar() {}
+}
diff --git a/src/test/test3/PublicInner.java b/src/test/test3/PublicInner.java
new file mode 100644
index 0000000..72cf634
--- /dev/null
+++ b/src/test/test3/PublicInner.java
@@ -0,0 +1,13 @@
+package test3;
+
+class PublicInner2 {
+ public static class PS {
+ int j;
+ }
+
+ int i;
+}
+
+public class PublicInner {
+ int i;
+}
diff --git a/src/test/test3/RecReplace.java b/src/test/test3/RecReplace.java
new file mode 100644
index 0000000..60fa44a
--- /dev/null
+++ b/src/test/test3/RecReplace.java
@@ -0,0 +1,20 @@
+package test3;
+
+public class RecReplace {
+ public int run() {
+ double d = 4.0;
+ int i = foo(3.0);
+ int j = bar(d);
+ return i + j;
+ }
+
+ public int run2() {
+ double d = 4.0;
+ int i = foo(3.0);
+ int j = bar(d);
+ return i + j;
+ }
+
+ int foo(double d) { return (int)d; }
+ int bar(double d) { return (int)d + 1; }
+}
diff --git a/src/test/test3/RecReplace2.java b/src/test/test3/RecReplace2.java
new file mode 100644
index 0000000..6db2bee
--- /dev/null
+++ b/src/test/test3/RecReplace2.java
@@ -0,0 +1,19 @@
+package test3;
+
+public class RecReplace2 {
+ int value = 3;
+ int value2 = 9;
+ int foo(int k) { return k + 1; }
+ public int run() {
+ value2 = 0;
+ value = 7;
+ value2 += 10;
+ return value + value2;
+ }
+ public int run2() {
+ value2 = 0;
+ value = 7;
+ value2 += 10;
+ return value + value2;
+ }
+}
diff --git a/src/test/test3/ReplaceNew.java b/src/test/test3/ReplaceNew.java
new file mode 100644
index 0000000..8f8a786
--- /dev/null
+++ b/src/test/test3/ReplaceNew.java
@@ -0,0 +1,20 @@
+package test3;
+
+public class ReplaceNew {
+ public ReplaceNew() {}
+ public ReplaceNew(String s) {}
+ int i = 0;
+ public int run() {
+ i = 3;
+ @SuppressWarnings("unused")
+ ReplaceNew s = new ReplaceNew();
+ new ReplaceNew();
+ return i;
+ }
+
+ static int j = 0;
+ public int run2() {
+ new ReplaceNew(new String("test"));
+ return j;
+ }
+}
diff --git a/src/test/test3/SetModifiers.java b/src/test/test3/SetModifiers.java
new file mode 100644
index 0000000..cf5706d
--- /dev/null
+++ b/src/test/test3/SetModifiers.java
@@ -0,0 +1,8 @@
+package test3;
+
+public class SetModifiers {
+ public int m;
+ public static final class A {
+ public int a;
+ }
+}
diff --git a/src/test/test3/SigAttribute.java b/src/test/test3/SigAttribute.java
new file mode 100644
index 0000000..83527f8
--- /dev/null
+++ b/src/test/test3/SigAttribute.java
@@ -0,0 +1,6 @@
+package test3;
+
+public class SigAttribute<T> {
+ T value;
+ T get(T t) { return t; }
+}
diff --git a/src/test/test3/StrBuild.java b/src/test/test3/StrBuild.java
new file mode 100644
index 0000000..fbb9803
--- /dev/null
+++ b/src/test/test3/StrBuild.java
@@ -0,0 +1,8 @@
+package test3;
+
+public class StrBuild {
+ public int test() {
+ StringBuilder sb = new StringBuilder("test");
+ return sb.charAt(0);
+ }
+}
diff --git a/src/test/test3/SubValue.java b/src/test/test3/SubValue.java
new file mode 100644
index 0000000..33e1c8e
--- /dev/null
+++ b/src/test/test3/SubValue.java
@@ -0,0 +1,15 @@
+package test3;
+
+class SuperValue {
+ int i;
+}
+
+public class SubValue extends SuperValue {
+ public SubValue after(SuperValue ret, SuperValue sup, SuperValue sup2) {
+ return null;
+ }
+
+ public SubValue after(SuperValue ret, SubValue sup, SuperValue sub) {
+ return new SubValue();
+ }
+}
diff --git a/src/test/test3/Superclass.java b/src/test/test3/Superclass.java
new file mode 100644
index 0000000..3240783
--- /dev/null
+++ b/src/test/test3/Superclass.java
@@ -0,0 +1,15 @@
+package test3;
+
+class Superclass2 {
+ public int foo() { return 1; }
+ public int bar() { return 10; }
+}
+
+class Superclass3 extends Superclass2 {
+ public int test() { return foo(); }
+ public int bar() { return 20; }
+}
+
+public class Superclass extends Superclass2 {
+ public int foo() { return super.foo() + super.bar(); }
+}
diff --git a/src/test/test3/Switch.java b/src/test/test3/Switch.java
new file mode 100644
index 0000000..636e0fa
--- /dev/null
+++ b/src/test/test3/Switch.java
@@ -0,0 +1,30391 @@
+package test3;
+
+public class Switch
+{
+ public int test() {
+ Switch b = new Switch();
+ b.foo();
+ return 0;
+ }
+
+ protected void foo()
+ {
+ int j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+ j = 4;
+
+ switch (1)
+ {
+ default:
+ }
+ }
+} \ No newline at end of file
diff --git a/src/test/test3/TransNewClass.java b/src/test/test3/TransNewClass.java
new file mode 100644
index 0000000..a8a7da1
--- /dev/null
+++ b/src/test/test3/TransNewClass.java
@@ -0,0 +1,37 @@
+package test3;
+
+class TransNewClassOld {
+ int k = 1;
+ TransNewClassOld() {}
+ TransNewClassOld(int i) { k = i; }
+ TransNewClassOld(TransNewClassOld obj) { k = obj.k; }
+}
+
+class TransNewClassNew extends TransNewClassOld {
+ TransNewClassNew() { k = 10; }
+ TransNewClassNew(int i) { k = i * 10; }
+ TransNewClassNew(TransNewClassOld obj) { k = obj.k * 2; }
+}
+
+class TransNewClassNot extends TransNewClassOld {
+ TransNewClassNot() { k = 100; }
+}
+
+public class TransNewClass {
+ public static class TransNewClass2 {
+ public int test() {
+ TransNewClassOld obj = new TransNewClassOld();
+ TransNewClassOld obj2 = new TransNewClassOld();
+ TransNewClassOld obj3 = new TransNewClassOld(3);
+ return obj.k + obj2.k + obj3.k;
+ }
+ }
+
+ public int test() {
+ TransNewClassOld obj = new TransNewClassOld();
+ TransNewClassOld obj2 = new TransNewClassOld(4);
+ TransNewClassOld obj3 = new TransNewClassNot();
+ TransNewClassOld obj4 = new TransNewClassOld(new TransNewClassOld());
+ return obj.k + obj2.k + obj3.k + obj4.k;
+ }
+}
diff --git a/src/test/test3/TransformRead.java b/src/test/test3/TransformRead.java
new file mode 100644
index 0000000..0075a23
--- /dev/null
+++ b/src/test/test3/TransformRead.java
@@ -0,0 +1,15 @@
+package test3;
+
+class TransformRead1 {
+ public int value = 1;
+ public int value2 = 10;
+}
+
+public class TransformRead extends TransformRead1 {
+ public int value = 100;
+ public int foo() {
+ return value + value2 + super.value;
+ }
+ public static int getValue(Object obj) { return 1000; }
+ public static int getValue2(Object obj) { return 10000; }
+}
diff --git a/src/test/test3/Unique.java b/src/test/test3/Unique.java
new file mode 100644
index 0000000..5dc8da3
--- /dev/null
+++ b/src/test/test3/Unique.java
@@ -0,0 +1,24 @@
+package test3;
+
+interface Unique5 {
+ void bar100();
+ void bar101b();
+}
+
+interface Unique4 extends Unique5 {
+ void bar();
+}
+
+abstract class Unique3 implements Unique4 {
+ abstract void foo();
+}
+
+class Unique2 {
+ void foo() {}
+ void foo100() {}
+ void foo101() {}
+}
+
+public class Unique extends Unique2 {
+ void foo() {}
+}
diff --git a/src/test/test3/Visible.java b/src/test/test3/Visible.java
new file mode 100644
index 0000000..d3397f1
--- /dev/null
+++ b/src/test/test3/Visible.java
@@ -0,0 +1,17 @@
+package test3;
+
+@SuppressWarnings("unused")
+class Visible2 {
+ public int pub;
+ protected int pro;
+ private int pri;
+ int pack;
+}
+
+@SuppressWarnings("unused")
+public class Visible {
+ public int pub;
+ protected int pro;
+ private int pri;
+ int pack;
+}
diff --git a/src/test/test3/VisibleAnno.java b/src/test/test3/VisibleAnno.java
new file mode 100644
index 0000000..37f7c23
--- /dev/null
+++ b/src/test/test3/VisibleAnno.java
@@ -0,0 +1,7 @@
+package test3;
+
+import java.lang.annotation.*;
+
+@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+public @interface VisibleAnno {
+}
diff --git a/src/test/test3/VoidReturn.java b/src/test/test3/VoidReturn.java
new file mode 100644
index 0000000..b084181
--- /dev/null
+++ b/src/test/test3/VoidReturn.java
@@ -0,0 +1,5 @@
+package test3;
+
+public class VoidReturn {
+ public void foo() {}
+}
diff --git a/src/test/test3/sub/SubPackName.java b/src/test/test3/sub/SubPackName.java
new file mode 100644
index 0000000..3899e63
--- /dev/null
+++ b/src/test/test3/sub/SubPackName.java
@@ -0,0 +1,5 @@
+package test3.sub;
+
+public class SubPackName {
+ public static int get() { return 5; }
+}
diff --git a/src/test/test3/sub/Visible.java b/src/test/test3/sub/Visible.java
new file mode 100644
index 0000000..5dd3c0e
--- /dev/null
+++ b/src/test/test3/sub/Visible.java
@@ -0,0 +1,12 @@
+package test3.sub;
+
+class Visible2 extends test3.Visible {
+ public int pub2;
+}
+
+public class Visible {
+ public int pub;
+ protected int pro;
+ private int pri;
+ int pack;
+}
diff --git a/src/test/test4/Aaload.java b/src/test/test4/Aaload.java
new file mode 100644
index 0000000..5299b48
--- /dev/null
+++ b/src/test/test4/Aaload.java
@@ -0,0 +1,15 @@
+package test4;
+import java.awt.MenuItem;
+
+public class Aaload {
+ static void narf() {
+ String[] list = null;
+ for (int i = 0; i < list.length; i++) {
+ String name = list[i];
+ if (name.endsWith(".txt")) {
+ MenuItem item = new MenuItem(name);
+ item.addActionListener(null);
+ }
+ }
+ }
+}
diff --git a/src/test/test4/AfterTest.java b/src/test/test4/AfterTest.java
new file mode 100644
index 0000000..3ba3650
--- /dev/null
+++ b/src/test/test4/AfterTest.java
@@ -0,0 +1,119 @@
+package test4;
+
+public class AfterTest {
+ public void print() { System.out.println("test4.AfterTest"); }
+
+ public int test1() { return m1(10) + m1(-10); }
+
+ public int m1(int i) {
+ if (i > 0)
+ i = i + 10;
+ else
+ return -i;
+
+ i = i + 100;
+ return i + 1;
+ }
+
+ public int test2() throws Exception { return m2(1); }
+
+ public int m2(int i) throws Exception {
+ if (i > 10)
+ throw new Exception();
+ else if (i > 0)
+ i = i + 10;
+ else
+ return -i;
+
+ i = i + 100;
+ return i + 1;
+ }
+
+ public int test3() throws Exception { return m3(-10); }
+
+ public int m3(int i) throws Exception {
+ if (i > 10)
+ throw new Exception();
+ else if (i > 0)
+ i = i + 10;
+ else
+ return -i;
+
+ i = i + 100;
+ throw new Exception();
+ }
+
+ public int test4() throws Exception {
+ try {
+ return m4(-10);
+ }
+ catch (Exception e) {
+ return 100;
+ }
+ }
+
+ public int m4(int i) throws Exception {
+ if (i > 0)
+ i = i + 10;
+
+ i = i + 100;
+ throw new Exception();
+ }
+
+ public int test11() { return mm1(10) + mm1(-10); }
+
+ public int mm1(int i) {
+ if (i > 0)
+ i = i + 10;
+ else
+ return -i;
+
+ i = i + 100;
+ return i + 1;
+ }
+
+ public int test22() throws Exception { return mm2(1); }
+
+ public int mm2(int i) throws Exception {
+ if (i > 10)
+ throw new Exception();
+ else if (i > 0)
+ i = i + 10;
+ else
+ return -i;
+
+ i = i + 100;
+ return i + 1;
+ }
+
+ public int test33() throws Exception { return mm3(-10); }
+
+ public int mm3(int i) throws Exception {
+ if (i > 10)
+ throw new Exception();
+ else if (i > 0)
+ i = i + 10;
+ else
+ return -i;
+
+ i = i + 100;
+ throw new Exception();
+ }
+
+ public int test44() throws Exception {
+ try {
+ return mm4(-10);
+ }
+ catch (Exception e) {
+ return 100;
+ }
+ }
+
+ public int mm4(int i) throws Exception {
+ if (i > 0)
+ i = i + 10;
+
+ i = i + 100;
+ throw new Exception();
+ }
+}
diff --git a/src/test/test4/Anno.java b/src/test/test4/Anno.java
new file mode 100644
index 0000000..336d4e6
--- /dev/null
+++ b/src/test/test4/Anno.java
@@ -0,0 +1,6 @@
+package test4;
+
+@Anno1 public class Anno {
+ @Anno1 public int value;
+ @Anno1 public int foo() { return 0; }
+}
diff --git a/src/test/test4/Anno1.java b/src/test/test4/Anno1.java
new file mode 100644
index 0000000..d368ecc
--- /dev/null
+++ b/src/test/test4/Anno1.java
@@ -0,0 +1,5 @@
+package test4;
+
+public @interface Anno1 {
+ String value() default "empty";
+}
diff --git a/src/test/test4/AnnoArg.java b/src/test/test4/AnnoArg.java
new file mode 100644
index 0000000..0184d41
--- /dev/null
+++ b/src/test/test4/AnnoArg.java
@@ -0,0 +1,18 @@
+package test4;
+
+public class AnnoArg {
+ public static @interface AnnoArgAt {
+ Class<? extends AnnoArg.A> value();
+ }
+
+ public static class A {
+ int baz() { return 1; }
+ }
+
+ public static class B extends A {
+ int baz() { return 2; }
+ }
+
+ @AnnoArgAt(B.class)
+ public int foo(int i) { return i; }
+}
diff --git a/src/test/test4/AnnoLoad.java b/src/test/test4/AnnoLoad.java
new file mode 100644
index 0000000..fba397c
--- /dev/null
+++ b/src/test/test4/AnnoLoad.java
@@ -0,0 +1,5 @@
+package test4;
+
+public class AnnoLoad {
+ public int foo() { return 0; }
+}
diff --git a/src/test/test4/CodeConv.java b/src/test/test4/CodeConv.java
new file mode 100644
index 0000000..548c1d2
--- /dev/null
+++ b/src/test/test4/CodeConv.java
@@ -0,0 +1,19 @@
+package test4;
+
+public class CodeConv {
+ int k = 1;
+ public int m1() {
+ return k + 10;
+ }
+ public int m2() {
+ return k + 100;
+ }
+ public static void m3(CodeConv cc) {
+ cc.k++;
+ }
+
+ public int run() {
+ int i = m1() * 1000 + m2();
+ return k + i * 10;
+ }
+}
diff --git a/src/test/test4/CodeConv2.java b/src/test/test4/CodeConv2.java
new file mode 100644
index 0000000..3a4ce03
--- /dev/null
+++ b/src/test/test4/CodeConv2.java
@@ -0,0 +1,39 @@
+package test4;
+
+public class CodeConv2 {
+ int field = 3;
+ static int sf = 1;
+
+ public int run() {
+ field = 7;
+ sf = 8;
+ switch (field) {
+ case 0:
+ field = 1;
+ break;
+ default:
+ }
+ int r = field * 10000 + sf;
+ switch (field) {
+ case 0:
+ field = 1;
+ break;
+ default:
+ }
+ return r;
+ }
+
+ public static void write(Object target, int value) {
+ if (target == null)
+ sf = value * 2;
+ else
+ ((CodeConv2)target).field = value * 2;
+ }
+
+ public static int read(Object target) {
+ if (target == null)
+ return sf * 100;
+ else
+ return ((CodeConv2)target).field * 100;
+ }
+}
diff --git a/src/test/test4/CtArrayTest.java b/src/test/test4/CtArrayTest.java
new file mode 100644
index 0000000..3967d4b
--- /dev/null
+++ b/src/test/test4/CtArrayTest.java
@@ -0,0 +1,4 @@
+package test4;
+
+public abstract class CtArrayTest {
+}
diff --git a/src/test/test4/DeclMethodsList.java b/src/test/test4/DeclMethodsList.java
new file mode 100644
index 0000000..e762e4c
--- /dev/null
+++ b/src/test/test4/DeclMethodsList.java
@@ -0,0 +1,7 @@
+package test4;
+
+public class DeclMethodsList {
+ public void foo() {}
+ int foo(int i) { return i; }
+ public void bar() {}
+}
diff --git a/src/test/test4/DefineClassCapability.java b/src/test/test4/DefineClassCapability.java
new file mode 100644
index 0000000..d0bef62
--- /dev/null
+++ b/src/test/test4/DefineClassCapability.java
@@ -0,0 +1,7 @@
+/*
+ * This is used as a capability for running CtClass#toClass().
+ */
+package test4;
+
+public class DefineClassCapability {
+}
diff --git a/src/test/test4/GapSwitch.java b/src/test/test4/GapSwitch.java
new file mode 100644
index 0000000..126bbdf
--- /dev/null
+++ b/src/test/test4/GapSwitch.java
@@ -0,0 +1,83 @@
+package test4;
+
+public class GapSwitch {
+ public int value;
+ public int foo(int i) { return i + 1; }
+ public int run() {
+ value = 0;
+ int k = 0;
+ k = foo(k);
+ switch (value) {
+ case 0:
+ k++;
+ break;
+ case 1:
+ k *= 10;
+ break;
+ default:
+ k *= 100;
+ break;
+ }
+
+ return k + value * 1000;
+ }
+
+ public int run2() {
+ value = 0;
+ int k = 0;
+ k = foo(k);
+ switch (value) {
+ case 10:
+ k++;
+ break;
+ case 1300:
+ k *= 10;
+ break;
+ default:
+ k *= 100;
+ break;
+ }
+
+ return k + value * 1000;
+ }
+
+ public int run3() {
+ value = 1;
+ int k = 0;
+ for (int i = 0; i < 2; i++) {
+ k = foo(k);
+ switch (value) {
+ case 10:
+ k++;
+ k = foo(k);
+ break;
+ case 1300:
+ k *= 100;
+ k = foo(k);
+ break;
+ default:
+ k *= 10;
+ k = foo(k);
+ break;
+ }
+
+ k = foo(k);
+ switch (value) {
+ case 10:
+ k++;
+ k = foo(k);
+ break;
+ case 13:
+ k *= 100;
+ k = foo(k);
+ break;
+ default:
+ k *= 10;
+ k = foo(k);
+ break;
+ }
+ }
+
+ return k + value;
+ }
+}
diff --git a/src/test/test4/GetAllRef.java b/src/test/test4/GetAllRef.java
new file mode 100644
index 0000000..c19bfab
--- /dev/null
+++ b/src/test/test4/GetAllRef.java
@@ -0,0 +1,33 @@
+package test4;
+
+enum GetAllRefEnum { A, B };
+
+@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@interface GetAllRefAnno {
+ GetAllRefEnum getA();
+ Class getC();
+}
+
+@interface GetAllRefAnnoC {}
+
+@interface GetAllRefAnnoC2 {}
+
+@interface GetAllRefAnnoC3 {}
+
+@interface GetAllRefAnnoC4 {}
+
+@GetAllRefAnno(getA = GetAllRefEnum.A, getC = String.class)
+public class GetAllRef {
+}
+
+@GetAllRefAnno(getA = GetAllRefEnum.A, getC = String.class)
+class GetAllRefB {
+}
+
+@GetAllRefAnno(getA = GetAllRefEnum.A, getC = String.class)
+class GetAllRefC {
+ void bar(@GetAllRefAnnoC3 int i, int j,
+ @GetAllRefAnnoC2 @GetAllRefAnnoC4 boolean b) {}
+ @GetAllRefAnnoC void foo() {}
+ @GetAllRefAnnoC2 int value;
+}
diff --git a/src/test/test4/GetAllRefAnno2.java b/src/test/test4/GetAllRefAnno2.java
new file mode 100644
index 0000000..dcb0956
--- /dev/null
+++ b/src/test/test4/GetAllRefAnno2.java
@@ -0,0 +1,6 @@
+package test4;
+
+public @interface GetAllRefAnno2 {
+ GetAllRefEnum2 getA();
+ Class getC();
+}
diff --git a/src/test/test4/GetAllRefEnum2.java b/src/test/test4/GetAllRefEnum2.java
new file mode 100644
index 0000000..d6cf629
--- /dev/null
+++ b/src/test/test4/GetAllRefEnum2.java
@@ -0,0 +1,3 @@
+package test4;
+
+public enum GetAllRefEnum2 { A, B }
diff --git a/src/test/test4/GetAllRefInnerTest.java b/src/test/test4/GetAllRefInnerTest.java
new file mode 100644
index 0000000..a4921ca
--- /dev/null
+++ b/src/test/test4/GetAllRefInnerTest.java
@@ -0,0 +1,29 @@
+package test4;
+
+class GetAllRefInnerTest2<T> {
+ Class clazz;
+ T value;
+ void foo(T t) { value = t; }
+ Object poi(T t) {
+ return new Object() {
+ public String toString(T t) { return this.getClass().toString(); }
+ };
+ }
+}
+
+public class GetAllRefInnerTest<T> {
+ public T bar(T b) {
+ Object obj = new GetAllRefInnerTest2<java.util.HashMap>() {
+ void foo(java.util.HashMap a) { value = null; String s = clazz.toString() + a.toString(); }
+ };
+ return b;
+ }
+ public Object foo() {
+ return new java.util.HashSet<String>() {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ public String toString() { return this.getClass().toString(); }
+ };
+ }
+}
diff --git a/src/test/test4/GetFieldDesc.java b/src/test/test4/GetFieldDesc.java
new file mode 100644
index 0000000..605e2ad
--- /dev/null
+++ b/src/test/test4/GetFieldDesc.java
@@ -0,0 +1,11 @@
+package test4;
+
+public class GetFieldDesc {
+ public int f;
+ public String s;
+}
+
+class GetFieldDescSub extends GetFieldDesc {
+ public int f;
+ public int s;
+}
diff --git a/src/test/test4/ImportPac.java b/src/test/test4/ImportPac.java
new file mode 100644
index 0000000..c6127c9
--- /dev/null
+++ b/src/test/test4/ImportPac.java
@@ -0,0 +1,4 @@
+package test4;
+
+public class ImportPac {
+}
diff --git a/src/test/test4/InvokeDyn.java b/src/test/test4/InvokeDyn.java
new file mode 100644
index 0000000..fbcba64
--- /dev/null
+++ b/src/test/test4/InvokeDyn.java
@@ -0,0 +1,26 @@
+package test4;
+
+import java.lang.invoke.*;
+
+public class InvokeDyn {
+ public static int test9(int i, String s) { return 9; }
+ public int test8(int i, String s) { return 8; }
+
+ public static CallSite boot(MethodHandles.Lookup caller, String name, MethodType type)
+ throws NoSuchMethodException, IllegalAccessException
+ {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ Class thisClass = lookup.lookupClass();
+ MethodHandle method = lookup.findStatic(thisClass, "test9", MethodType.methodType(int.class, int.class, String.class));
+ return new ConstantCallSite(method);
+ }
+
+ public CallSite boot2(MethodHandles.Lookup caller, String name, MethodType type)
+ throws NoSuchMethodException, IllegalAccessException
+ {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ Class thisClass = lookup.lookupClass();
+ MethodHandle method = lookup.findVirtual(thisClass, "test8", MethodType.methodType(int.class, int.class, String.class));
+ return new ConstantCallSite(method.asType(MethodType.methodType(int.class, Object.class, int.class, String.class)));
+ }
+}
diff --git a/src/test/test4/JIRA152.java b/src/test/test4/JIRA152.java
new file mode 100644
index 0000000..8fba32a
--- /dev/null
+++ b/src/test/test4/JIRA152.java
@@ -0,0 +1,60 @@
+package test4;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public class JIRA152 {
+ public int foo(int i) { return i; }
+ public int bar(int j) { return j; }
+ public int tested(int k) {
+ String[] p;
+ if (k > 0)
+ p = new String[1];
+ else
+ p = null;
+
+ if (p != null)
+ while (k < p.length)
+ k++;
+
+ return 0;
+ }
+
+ public String put(String s, Object obj) {
+ return s;
+ }
+
+ private static Map<String, String[]> buildColumnOverride(JIRA152 element, String path) {
+ Map<String, String[]> columnOverride = new HashMap<String, String[]>();
+ if ( element == null ) return null;
+ String singleOverride = element.toString();
+ String multipleOverrides = element.toString();
+ String[] overrides;
+ if ( singleOverride != null ) {
+ overrides = new String[] { singleOverride };
+ }
+ /*else if ( multipleOverrides != null ) {
+ // overrides = columnOverride.get("foo");
+ overrides = null;
+ }*/
+ else {
+ overrides = null;
+ }
+
+ if ( overrides != null ) {
+ for (String depAttr : overrides) {
+ columnOverride.put(
+ element.put(path, depAttr.getClass()),
+ new String[] { depAttr.toLowerCase() }
+ );
+ //columnOverride.put("a", new String[1]);
+ }
+ }
+ return columnOverride;
+ }
+
+ public int test() {
+ Map<String,String[]> map = buildColumnOverride(this, "foo");
+ return map.size();
+ }
+}
diff --git a/src/test/test4/JIRA158.java b/src/test/test4/JIRA158.java
new file mode 100644
index 0000000..290f805
--- /dev/null
+++ b/src/test/test4/JIRA158.java
@@ -0,0 +1,30 @@
+package test4;
+
+interface JIRA158Intf {
+ int foo(long[] j, double[] d);
+ int bar(long j, double d);
+}
+
+class JIRA158Impl implements JIRA158Intf {
+ public int foo(long[] j, double[] d) {
+ return 7;
+ }
+ public int bar(long j, double d) {
+ return 8;
+ }
+}
+
+public class JIRA158 {
+ long[] jj;
+ double[] dd;
+ long j;
+ double d;
+ JIRA158Intf obj;
+ public JIRA158() {
+ jj = new long[1];
+ dd = new double[1];
+ j = 3L;
+ d = 3.0;
+ obj = new JIRA158Impl();
+ }
+}
diff --git a/src/test/test4/JIRA166.java b/src/test/test4/JIRA166.java
new file mode 100644
index 0000000..bbd46ef
--- /dev/null
+++ b/src/test/test4/JIRA166.java
@@ -0,0 +1,12 @@
+package test4;
+
+public class JIRA166 {
+ String length = "jira166";
+ JIRA166 self() { return this; }
+ void print() { System.out.println(length); }
+ public int run() {
+ print();
+ System.out.println(length);
+ return 1;
+ }
+}
diff --git a/src/test/test4/JIRA181.java b/src/test/test4/JIRA181.java
new file mode 100644
index 0000000..7c0b093
--- /dev/null
+++ b/src/test/test4/JIRA181.java
@@ -0,0 +1,32 @@
+package test4;
+
+import java.util.ArrayList;
+
+public class JIRA181<T extends Number> extends ArrayList<T> {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ public @interface Condition {
+ Class<? extends ICondition> condition();
+ }
+
+ public @interface Condition2 {
+ Class<?> condition();
+ }
+
+ @Condition(condition = B.class)
+ public Object aField;
+
+ @Condition2(condition = B[].class)
+ public Object aField2;
+
+ public interface ICondition {
+ boolean match(Object src);
+ }
+
+ private class B implements ICondition {
+ public boolean match(Object src) {
+ return JIRA181.this.size() > 0;
+ }
+ }
+}
diff --git a/src/test/test4/JIRA181b.java b/src/test/test4/JIRA181b.java
new file mode 100644
index 0000000..133931a
--- /dev/null
+++ b/src/test/test4/JIRA181b.java
@@ -0,0 +1,12 @@
+package test4;
+
+public class JIRA181b {
+ public @interface Condition {
+ Class<?> condition();
+ }
+
+ @Condition(condition = String.class)
+ public Object aField;
+ @Condition(condition = void.class)
+ public Object aField2;
+}
diff --git a/src/test/test4/JIRA186.java b/src/test/test4/JIRA186.java
new file mode 100644
index 0000000..41935a7
--- /dev/null
+++ b/src/test/test4/JIRA186.java
@@ -0,0 +1,7 @@
+package test4;
+
+public class JIRA186 {
+ public int test() {
+ return 1;
+ }
+}
diff --git a/src/test/test4/JIRA195.java b/src/test/test4/JIRA195.java
new file mode 100644
index 0000000..13b91d1
--- /dev/null
+++ b/src/test/test4/JIRA195.java
@@ -0,0 +1,31 @@
+package test4;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+
+public class JIRA195 {
+ public int run() { return test(3); }
+
+ public int test(int i) {
+ try {}
+ catch (Throwable t) {}
+ finally {
+ i = incByOne(i);
+ }
+
+ return i;
+ }
+
+ private int incByOne(int i) {
+ return i + 1;
+ }
+
+ public static void main(String[] args) throws Exception {
+ ClassPool cp = new ClassPool();
+ cp.appendClassPath("./target/test-classes");
+ CtClass cc = cp.get("test4.JIRA195");
+ CtMethod mth = cc.getDeclaredMethod("test");
+ mth.getMethodInfo().rebuildStackMap(cc.getClassPool());
+ }
+}
diff --git a/src/test/test4/JIRA207.java b/src/test/test4/JIRA207.java
new file mode 100644
index 0000000..c0e6752
--- /dev/null
+++ b/src/test/test4/JIRA207.java
@@ -0,0 +1,51 @@
+package test4;
+
+public class JIRA207 {
+ public int run() {
+ int i = 3;
+ return foo(i);
+ }
+
+ public int foo(int i) {
+ int k = i + 3;
+ if (k > 0)
+ return k * k;
+ else
+ return k;
+ }
+
+ public int run2() {
+ int i = 0;
+ int p = i;
+ int q = p;
+ int r = q;
+ for (int k = 1; k < 3; ++k)
+ p += k;
+
+ for (int k = 3; k > 0; --k)
+ try {
+ foo(k);
+ p++;
+ }
+ finally {
+ p++;
+ }
+
+ try {
+ foo(p);
+ }
+ catch (RuntimeException e) {
+ if (p > 0)
+ throw e;
+ }
+
+ switch (p) {
+ case 1:
+ p = 100;
+ break;
+ default :
+ ++p;
+ }
+ return p + r;
+ }
+}
diff --git a/src/test/test4/JIRA212.java b/src/test/test4/JIRA212.java
new file mode 100644
index 0000000..4c532d0
--- /dev/null
+++ b/src/test/test4/JIRA212.java
@@ -0,0 +1,23 @@
+package test4;
+
+public class JIRA212 {
+ public Long id;
+ public String name;
+ public void test() {
+ JIRA212 object = new JIRA212();
+
+ object.name = "test"; // get or set string value
+
+ try { // in try
+ object.id = 100L; // get or set long value
+ String name = object.name; // get or set string value
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ public Long getId() { return id; }
+ public void setId(Long id) { this.id = id; }
+ public String getName() { return name; }
+ public void setName(String name) { this.name = name; }
+}
diff --git a/src/test/test4/JIRA220.java b/src/test/test4/JIRA220.java
new file mode 100644
index 0000000..01a9845
--- /dev/null
+++ b/src/test/test4/JIRA220.java
@@ -0,0 +1,13 @@
+package test4;
+
+interface JIRA220intf {
+ static void bar() {
+ // Do something
+ }
+}
+
+public class JIRA220 implements JIRA220intf {
+ public static void foo() {
+ JIRA220intf.bar();
+ }
+}
diff --git a/src/test/test4/JIRA93.java b/src/test/test4/JIRA93.java
new file mode 100644
index 0000000..2d94710
--- /dev/null
+++ b/src/test/test4/JIRA93.java
@@ -0,0 +1,5 @@
+package test4;
+
+public class JIRA93 {
+ public void foo() {}
+}
diff --git a/src/test/test4/LocalVars.java b/src/test/test4/LocalVars.java
new file mode 100644
index 0000000..17ab6cd
--- /dev/null
+++ b/src/test/test4/LocalVars.java
@@ -0,0 +1,60 @@
+package test4;
+
+public class LocalVars {
+ public int run() {
+ int i = 0;
+ int p = i;
+ int q = p;
+ int r = q;
+ for (int k = 1; k < 3; ++k)
+ p += k;
+
+ for (int k = 3; k > 0; --k)
+ try {
+ foo(k);
+ p++;
+ }
+ finally {
+ p++;
+ }
+
+ switch (p) {
+ case 1:
+ p = 100;
+ break;
+ default :
+ ++p;
+ }
+ return p + r;
+ }
+
+ public int run2() {
+ int i = 0;
+ int p = i;
+ int q = p;
+ int r = q;
+ for (int k = 1; k < 3; ++k)
+ p += k;
+
+ for (int k = 3; k > 0; --k)
+ try {
+ foo(k);
+ p++;
+ }
+ finally {
+ p++;
+ }
+
+ switch (p) {
+ case 1:
+ p = 100;
+ break;
+ default :
+ ++p;
+ }
+
+ return p + r;
+ }
+
+ public int foo(int i) { return i; }
+}
diff --git a/src/test/test4/Lvtt.java b/src/test/test4/Lvtt.java
new file mode 100644
index 0000000..b9d72c5
--- /dev/null
+++ b/src/test/test4/Lvtt.java
@@ -0,0 +1,11 @@
+package test4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Lvtt {
+ public void run() {
+ List<String> s = new ArrayList<String>();
+ System.out.println(s);
+ }
+}
diff --git a/src/test/test4/MakeMethod.java b/src/test/test4/MakeMethod.java
new file mode 100644
index 0000000..4b9bb24
--- /dev/null
+++ b/src/test/test4/MakeMethod.java
@@ -0,0 +1,7 @@
+package test4;
+
+public class MakeMethod {
+ public static final String foo() {
+ return "foo";
+ }
+}
diff --git a/src/test/test4/MethodParamTest.java b/src/test/test4/MethodParamTest.java
new file mode 100644
index 0000000..9f64e15
--- /dev/null
+++ b/src/test/test4/MethodParamTest.java
@@ -0,0 +1,5 @@
+package test4;
+
+public class MethodParamTest {
+ public void test(int i, String s) {}
+}
diff --git a/src/test/test4/MultiCatch.java b/src/test/test4/MultiCatch.java
new file mode 100644
index 0000000..142e324
--- /dev/null
+++ b/src/test/test4/MultiCatch.java
@@ -0,0 +1,23 @@
+package test4;
+
+public class MultiCatch {
+ public void print() { System.out.println("MultiCatch"); }
+ public int test1() { return m1(1); }
+ public int m1(int i) {
+ // Java 7 syntax
+ try {
+ return foo(i);
+ }
+ catch (java.io.IOException | NullPointerException e) {
+ return e.getMessage().length();
+ }
+ }
+ public int foo(int i) throws java.io.IOException {
+ if (i < 0)
+ throw new java.io.IOException("negative");
+ else if (i < 10)
+ throw new NullPointerException("less than 10");
+ else
+ return i;
+ }
+}
diff --git a/src/test/test4/NestedClass.java b/src/test/test4/NestedClass.java
new file mode 100644
index 0000000..3e18f7a
--- /dev/null
+++ b/src/test/test4/NestedClass.java
@@ -0,0 +1,29 @@
+package test4;
+
+public class NestedClass {
+ public S.S2 s2;
+ public class N {
+ public String toString() { return "N"; }
+ }
+ public static class S {
+ public String toString() { return "S"; }
+ public static class S2 {
+ public String toString() { return "S2"; }
+ }
+ }
+ public Object foo() {
+ class In {
+ public String toString() { return "S"; }
+ public String toString2() { return new S().toString(); }
+ }
+ return new Object() {
+ public String toString() {
+ return new Object() {
+ public String toString() {
+ return "ok";
+ }
+ }.toString();
+ }
+ };
+ }
+}
diff --git a/src/test/test4/NewImportPac.java b/src/test/test4/NewImportPac.java
new file mode 100644
index 0000000..fa9f4ac
--- /dev/null
+++ b/src/test/test4/NewImportPac.java
@@ -0,0 +1,4 @@
+package test4;
+
+public class NewImportPac {
+}
diff --git a/src/test/test4/NewRemover.java b/src/test/test4/NewRemover.java
new file mode 100644
index 0000000..ff57ce2
--- /dev/null
+++ b/src/test/test4/NewRemover.java
@@ -0,0 +1,30 @@
+package test4;
+
+public class NewRemover {
+ static NewRemover obj = new NewRemover(7);
+ int value;
+ static int value2 = 5;
+
+ public NewRemover() {
+ this(3);
+ }
+
+ public NewRemover(int k) {
+ value = k;
+ }
+
+ public int run() {
+ return make();
+ }
+
+ public int make() {
+ NewRemover nr = new NewRemover(value2 > 0 ? 3 : 0);
+ return nr.value;
+ }
+
+ public static NewRemover make2(int z) {
+ System.out.println("make2 " + z);
+ obj.value += z;
+ return obj;
+ }
+}
diff --git a/src/test/test4/Rename.java b/src/test/test4/Rename.java
new file mode 100644
index 0000000..b2ff794
--- /dev/null
+++ b/src/test/test4/Rename.java
@@ -0,0 +1,30 @@
+package test4;
+
+interface IRename {
+ Rename foo(Rename r);
+}
+
+class RenameB {
+ int foo() { return 10; }
+}
+
+public class Rename implements IRename {
+ int value = 3;
+ Rename next = null;
+
+ public Rename foo(Rename r) {
+ Rename k = r;
+ if (k == null)
+ return null;
+ else
+ return k.next;
+ }
+
+ public int run() {
+ next = new Rename();
+ next.value = 4;
+ RenameB rb = new RenameB();
+ return foo(this).value + rb.foo();
+ }
+}
+
diff --git a/src/test/test4/Signature.java b/src/test/test4/Signature.java
new file mode 100644
index 0000000..f6028a1
--- /dev/null
+++ b/src/test/test4/Signature.java
@@ -0,0 +1,16 @@
+package test4;
+
+public class Signature<T> {
+ public static class Foo {
+ int value;
+ }
+
+ public int run() {
+ Signature<String> s = new Signature<String>();
+ return s.foo(Integer.valueOf(3), "foo", s, null).length();
+ }
+
+ <S> T foo(S s, T t, Signature<T> signature, Signature<Signature<String>> v) {
+ return t;
+ }
+}
diff --git a/src/test/test4/VarArgs.java b/src/test/test4/VarArgs.java
new file mode 100644
index 0000000..53c0407
--- /dev/null
+++ b/src/test/test4/VarArgs.java
@@ -0,0 +1,15 @@
+package test4;
+
+public class VarArgs {
+ public int test() {
+ return goo(1, 2, 3) + goo(1, "a", "b", "c");
+ }
+
+ public int goo(int i, int... k) {
+ return k.length;
+ }
+
+ public int goo(int i, String... k) {
+ return k.length;
+ }
+}
diff --git a/src/test/test4/length.java b/src/test/test4/length.java
new file mode 100644
index 0000000..8158f4c
--- /dev/null
+++ b/src/test/test4/length.java
@@ -0,0 +1,5 @@
+package test4;
+
+public class length {
+ public static int m() { return 7; }
+}
diff --git a/src/test/test5/BoolTest.java b/src/test/test5/BoolTest.java
new file mode 100644
index 0000000..0d9ca15
--- /dev/null
+++ b/src/test/test5/BoolTest.java
@@ -0,0 +1,15 @@
+package test5;
+
+public class BoolTest {
+ static boolean i = false;
+ public boolean test() {
+ return i;
+ }
+ public boolean foo(boolean b) { return b; }
+ public int run() {
+ if (test())
+ return 1;
+ else
+ return 0;
+ }
+}
diff --git a/src/test/test5/DefaultMethod.java b/src/test/test5/DefaultMethod.java
new file mode 100644
index 0000000..7cabb1f
--- /dev/null
+++ b/src/test/test5/DefaultMethod.java
@@ -0,0 +1,19 @@
+package test5;
+
+interface DefaultMethodSupIntf {
+ default int foo() { return 0; }
+}
+
+interface DefaultMethodIntf extends DefaultMethodSupIntf {
+ default int foo() { return 1; }
+ static int baz() { return 10; }
+}
+
+public class DefaultMethod implements DefaultMethodIntf {
+ public int bar() { return DefaultMethodIntf.super.foo(); }
+
+ public static void main(String[] args) {
+ int i = new DefaultMethod().bar() + new DefaultMethod().foo() + DefaultMethodIntf.baz();
+ System.out.println(i);
+ }
+}
diff --git a/src/test/test5/DefineClassCapability.java b/src/test/test5/DefineClassCapability.java
new file mode 100644
index 0000000..1bb573c
--- /dev/null
+++ b/src/test/test5/DefineClassCapability.java
@@ -0,0 +1,7 @@
+/*
+ * This is used as a capability for running CtClass#toClass().
+ */
+package test5;
+
+public class DefineClassCapability {
+}
diff --git a/src/test/test5/Entity.java b/src/test/test5/Entity.java
new file mode 100644
index 0000000..1060c84
--- /dev/null
+++ b/src/test/test5/Entity.java
@@ -0,0 +1,9 @@
+package test5;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Entity {
+ int[] value();
+}
diff --git a/src/test/test5/InnerClassRemove.java b/src/test/test5/InnerClassRemove.java
new file mode 100644
index 0000000..8fff676
--- /dev/null
+++ b/src/test/test5/InnerClassRemove.java
@@ -0,0 +1,8 @@
+package test5;
+
+public class InnerClassRemove {
+ public class A { int a; }
+ class B { int b; }
+ static class C { int c; }
+ public int run() { return 1; }
+}
diff --git a/src/test/test5/InnerModifier.java b/src/test/test5/InnerModifier.java
new file mode 100644
index 0000000..5093e1f
--- /dev/null
+++ b/src/test/test5/InnerModifier.java
@@ -0,0 +1,6 @@
+package test5;
+
+public class InnerModifier {
+ protected class NonStatic {}
+ static class Static {}
+}
diff --git a/src/test/test5/InnerModifier2.java b/src/test/test5/InnerModifier2.java
new file mode 100644
index 0000000..009a032
--- /dev/null
+++ b/src/test/test5/InnerModifier2.java
@@ -0,0 +1,13 @@
+package test5;
+
+@SuppressWarnings("unused")
+public class InnerModifier2 {
+ public class Public {}
+ protected class Protected {}
+ private class Private {}
+ class Package {}
+ public static class PublicStatic {}
+ protected static class ProtectedStatic {}
+ private static class PrivateStatic {}
+ static class PackageStatic {}
+}
diff --git a/src/test/test5/InvalidCastDollar.java b/src/test/test5/InvalidCastDollar.java
new file mode 100644
index 0000000..9633189
--- /dev/null
+++ b/src/test/test5/InvalidCastDollar.java
@@ -0,0 +1,11 @@
+package test5;
+
+public class InvalidCastDollar {
+ public static byte[] arrayReturn() {
+ return new byte[12];
+ }
+
+ public static int intReturn() {
+ return 23;
+ }
+}
diff --git a/src/test/test5/Issue155.java b/src/test/test5/Issue155.java
new file mode 100644
index 0000000..3782e10
--- /dev/null
+++ b/src/test/test5/Issue155.java
@@ -0,0 +1,22 @@
+package test5;
+
+public class Issue155 {
+ public void bar() {}
+
+ public void foo() throws Throwable {
+ try {
+ bar();
+ } catch (java.lang.IllegalArgumentException e) {
+ bar();
+ }
+ }
+
+ public int test() throws Throwable {
+ foo();
+ return 1;
+ }
+
+ public static void main(String[] args) throws Throwable {
+ new Issue155().foo();
+ }
+}
diff --git a/src/test/test5/JIRA241.java b/src/test/test5/JIRA241.java
new file mode 100644
index 0000000..e6f8e95
--- /dev/null
+++ b/src/test/test5/JIRA241.java
@@ -0,0 +1,33 @@
+package test5;
+
+import java.util.Random;
+import javassist.*;
+
+public class JIRA241 {
+ public int run() {
+ test(this);
+ return 10;
+ }
+
+ public static void test(Object o) {
+ //part 1
+ if (o == null) {
+ return;
+ }
+
+ //part 2
+ int oper = new Random().nextInt();
+ switch (oper) {
+ case 1:
+ break;
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ ClassPool pool = ClassPool.getDefault();
+ CtClass cc = pool.get("test5.JIRA241");
+ CtMethod testMethod = cc.getMethod("test", "(Ljava/lang/Object;)V");
+ testMethod.insertAfter("System.out.println(\"inserted!\");");
+ cc.writeFile();
+ }
+}
diff --git a/src/test/test5/JIRA242.java b/src/test/test5/JIRA242.java
new file mode 100644
index 0000000..312e594
--- /dev/null
+++ b/src/test/test5/JIRA242.java
@@ -0,0 +1,25 @@
+package test5;
+
+public class JIRA242 {
+ static interface IBooleanSeries {
+ public void setValue(boolean value);
+ }
+
+ public static class BooleanDataSeries implements IBooleanSeries{
+ @Override
+ public void setValue(boolean value) {}
+ }
+
+ public static class Hello {
+ IBooleanSeries BOOL_SERIES;
+
+ public int say() {
+ System.out.println("Hello end :) ");
+ return 0;
+ }
+
+ public IBooleanSeries createBooleanSeriesStep() {
+ return new BooleanDataSeries();
+ }
+ }
+}
diff --git a/src/test/test5/JIRA246.java b/src/test/test5/JIRA246.java
new file mode 100644
index 0000000..da23f6e
--- /dev/null
+++ b/src/test/test5/JIRA246.java
@@ -0,0 +1,21 @@
+package test5;
+
+public class JIRA246 {
+ public interface Test {
+ default void defaultMethod() {
+ }
+ void test();
+ }
+
+ public interface IA {
+ default int get() {
+ return 0;
+ }
+ }
+
+ public static class A implements IA {
+ public int anotherGet() {
+ return 1;
+ }
+ }
+}
diff --git a/src/test/test5/JIRA248.java b/src/test/test5/JIRA248.java
new file mode 100644
index 0000000..5a33494
--- /dev/null
+++ b/src/test/test5/JIRA248.java
@@ -0,0 +1,22 @@
+package test5;
+
+interface JIRA248Intf {
+ default int foo() { return 1; }
+}
+
+interface JIRA248Intf2 {
+ default int baz() { return 40000; }
+}
+
+class JIRA248Sup2 {
+ public int bar() { return 200; }
+}
+
+class JIRA248Sup extends JIRA248Sup2 implements JIRA248Intf {
+}
+
+public class JIRA248 extends JIRA248Sup implements JIRA248Intf2 {
+ public int foo() { return 70; }
+ public int bar() { return 3000; }
+ public int baz() { return 500000; }
+}
diff --git a/src/test/test5/JIRA250Super.java b/src/test/test5/JIRA250Super.java
new file mode 100644
index 0000000..9c328ee
--- /dev/null
+++ b/src/test/test5/JIRA250Super.java
@@ -0,0 +1,17 @@
+package test5;
+
+interface JIRA250BarI {
+ int foo();
+}
+
+class JIRA250Bar implements JIRA250BarI {
+ public int foo() { return 1; }
+}
+
+interface JIRA250SuperI {
+ JIRA250BarI getBar();
+}
+
+public class JIRA250Super extends JIRA250Super2 implements JIRA250SuperI {
+}
+
diff --git a/src/test/test5/JIRA250Super2.java b/src/test/test5/JIRA250Super2.java
new file mode 100644
index 0000000..919b086
--- /dev/null
+++ b/src/test/test5/JIRA250Super2.java
@@ -0,0 +1,5 @@
+package test5;
+
+public class JIRA250Super2 {
+ public JIRA250Bar getBar() { return null; }
+}
diff --git a/src/test/test5/JavassistInvalidCastTest.java b/src/test/test5/JavassistInvalidCastTest.java
new file mode 100644
index 0000000..aa9682b
--- /dev/null
+++ b/src/test/test5/JavassistInvalidCastTest.java
@@ -0,0 +1,6 @@
+package test5;
+
+public class JavassistInvalidCastTest {
+ public void inspectReturn(String str) {}
+ public void inspectReturn(Object obj) {}
+}
diff --git a/src/test/test5/NestHost.java b/src/test/test5/NestHost.java
new file mode 100644
index 0000000..045d9d3
--- /dev/null
+++ b/src/test/test5/NestHost.java
@@ -0,0 +1,11 @@
+package test5;
+
+public class NestHost {
+ private int value;
+ public class Foo {
+ int foo() { return value++; }
+ }
+ public class Bar {
+ int bar() { return value++; }
+ }
+}
diff --git a/src/test/test5/NestHost2.java b/src/test/test5/NestHost2.java
new file mode 100644
index 0000000..571059b
--- /dev/null
+++ b/src/test/test5/NestHost2.java
@@ -0,0 +1,11 @@
+package test5;
+
+public class NestHost2 {
+ private int value;
+ public class Foo {
+ int foo() { return value++; }
+ }
+ public class Bar {
+ int bar() { return value++; }
+ }
+}
diff --git a/src/test/test5/ProceedDefault.java b/src/test/test5/ProceedDefault.java
new file mode 100644
index 0000000..18e2ee1
--- /dev/null
+++ b/src/test/test5/ProceedDefault.java
@@ -0,0 +1,13 @@
+package test5;
+
+interface ProceedDefaultI {
+ default int foo() { return 13; }
+}
+
+public class ProceedDefault implements ProceedDefaultI {
+ public int run() { return bar(); }
+ public int foo() { return 1700; }
+ public int bar() {
+ return foo() + ProceedDefaultI.super.foo();
+ }
+}
diff --git a/src/test/test5/RemoveAnnotation.java b/src/test/test5/RemoveAnnotation.java
new file mode 100644
index 0000000..97f7525
--- /dev/null
+++ b/src/test/test5/RemoveAnnotation.java
@@ -0,0 +1,20 @@
+package test5;
+
+@interface RemoveAnno1 {}
+
+@interface RemoveAnno2 {
+ int foo() default 3;
+}
+
+@RemoveAnno1 public class RemoveAnnotation {
+ @RemoveAnno1 @RemoveAnno2(foo=4)
+ int foo() { return 1; }
+
+ @RemoveAnno2
+ int bar() { return 2; }
+
+ @RemoveAnno1
+ int baz = 10;
+
+ public int run() { return foo() + bar(); }
+}
diff --git a/src/test/test5/StackmapWithArray83.java b/src/test/test5/StackmapWithArray83.java
new file mode 100644
index 0000000..c8333d8
--- /dev/null
+++ b/src/test/test5/StackmapWithArray83.java
@@ -0,0 +1,38 @@
+package test5;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class StackmapWithArray83 {
+ public int run() {
+ bytecodeVerifyError();
+ bytecodeVerifyError2();
+ return 1;
+ }
+
+ public void bytecodeVerifyError() {
+ List<Integer> test = new ArrayList<Integer>();
+ String[] newLine = new String[10];
+ for (Integer idx : test) {
+ String address = newLine[1];
+ int tabPos = -1;
+ if (tabPos != -1) {
+ address = address.substring(tabPos + 1);
+ }
+ newLine[4] = address;
+ }
+ }
+
+ public void bytecodeVerifyError2() {
+ List<Integer> test = new ArrayList<Integer>();
+ int[] newLine = new int[10];
+ for (Integer idx : test) {
+ int address = newLine[1];
+ int tabPos = -1;
+ if (tabPos != -1) {
+ address = address + tabPos;
+ }
+ newLine[4] = address;
+ }
+ }
+}
diff --git a/src/test/test5/TypeAnno.java b/src/test/test5/TypeAnno.java
new file mode 100644
index 0000000..a24d400
--- /dev/null
+++ b/src/test/test5/TypeAnno.java
@@ -0,0 +1,30 @@
+package test5;
+
+import java.util.ArrayList;
+
+class TypeAnnoSuper {}
+interface TypeAnnoI {}
+
+@TypeAnnoA
+public class TypeAnno<@TypeAnnoA TT extends @TypeAnnoA String> extends @TypeAnnoA TypeAnnoSuper implements @TypeAnnoA TypeAnnoI {
+ public @TypeAnnoA String foo(@TypeAnnoA int i) throws @TypeAnnoA Exception {
+ @TypeAnnoA String s = new @TypeAnnoA String("bar ");
+ Object t = s;
+ String ss = (@TypeAnnoA String)t;
+ ArrayList<@TypeAnnoA String> list = new ArrayList<@TypeAnnoA String>();
+ if (list instanceof /* @TypeAnnoA */ java.util.List)
+ System.out.println("ok");
+
+ try {
+ list.add(ss);
+ } catch (@TypeAnnoA RuntimeException e) {}
+ return "foo" + list.get(0) + i;
+ }
+
+ @TypeAnnoA double dvalue;
+ @TypeAnnoA int ivalue @TypeAnnoA [] @TypeAnnoA [] @TypeAnnoA [];
+
+ <@TypeAnnoA T extends @TypeAnnoA String> T bar(T t) { return t; }
+
+ @TypeAnnoA TT getNull() { return null; }
+}
diff --git a/src/test/test5/TypeAnnoA.java b/src/test/test5/TypeAnnoA.java
new file mode 100644
index 0000000..7526778
--- /dev/null
+++ b/src/test/test5/TypeAnnoA.java
@@ -0,0 +1,10 @@
+package test5;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE_USE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TypeAnnoA {}
diff --git a/src/test/test5/VarArgsMethod.java b/src/test/test5/VarArgsMethod.java
new file mode 100644
index 0000000..16055eb
--- /dev/null
+++ b/src/test/test5/VarArgsMethod.java
@@ -0,0 +1,6 @@
+package test5;
+
+public class VarArgsMethod {
+ public void foo(String... a) {}
+ public void bar(String s) {}
+}
diff --git a/src/test/testproxy/BridgeMethod.java b/src/test/testproxy/BridgeMethod.java
new file mode 100644
index 0000000..de199bf
--- /dev/null
+++ b/src/test/testproxy/BridgeMethod.java
@@ -0,0 +1,19 @@
+package testproxy;
+
+interface BridgeMethodInf {
+ public Long getId();
+ public void setId(Long id);
+ public Number m1();
+}
+
+abstract class BridgeMethodSuper<T> {
+ public abstract T id(T t);
+}
+
+public class BridgeMethod extends BridgeMethodSuper<String> implements BridgeMethodInf {
+ private Long id;
+ public Long getId() { return id; }
+ public void setId(Long id) { this.id = id; }
+ public Integer m1() { return 7; }
+ public String id(String s) { return s; }
+}
diff --git a/src/test/testproxy/ProxyFactoryPerformanceTest.java b/src/test/testproxy/ProxyFactoryPerformanceTest.java
new file mode 100644
index 0000000..df8bd8f
--- /dev/null
+++ b/src/test/testproxy/ProxyFactoryPerformanceTest.java
@@ -0,0 +1,200 @@
+package testproxy;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+/**
+import net.sf.cglib.proxy.CallbackFilter;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.InvocationHandler;
+import net.sf.cglib.proxy.NoOp;
+*/
+import javassist.util.proxy.MethodFilter;
+import javassist.util.proxy.MethodHandler;
+import javassist.util.proxy.ProxyFactory;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+@SuppressWarnings({"rawtypes","unchecked", "unused"})
+public class ProxyFactoryPerformanceTest extends TestCase {
+
+ public static final int COUNT = 100;
+
+ public static final int MAX_THREADS = 30;
+
+ static Throwable error = null;
+
+ public ProxyFactoryPerformanceTest() {}
+
+ public ProxyFactoryPerformanceTest(String name) {
+ super(name);
+ }
+
+ public void testJavassist() throws Throwable {
+ callCreateClass("javassist", ProxyMaker.class);
+ }
+
+ /**
+ public void testCglib() throws Exception {
+ callCreateClass("cglib", EnhancerUser.class);
+ }
+ */
+
+ public void callCreateClass(String translator, Class cl) throws Throwable {
+ error = null;
+ Thread[] threads = new Thread[MAX_THREADS];
+ for (int i = 0; i < threads.length; ++i) {
+ threads[i] = (Thread)cl.getDeclaredConstructor().newInstance();
+ }
+ long time = System.currentTimeMillis();
+ for (int i = 0; i < threads.length; ++i) {
+ threads[i].start();
+ }
+ for (int i = 0; i < threads.length; ++i) {
+ threads[i].join();
+ }
+ time = System.currentTimeMillis() - time;
+ System.out.println("ProxyFactoryPerformanceTest: " + translator + " time: " + time);
+ if (error != null)
+ throw error;
+ }
+
+ public static Test suite() {
+ return new TestSuite(ProxyFactoryPerformanceTest.class);
+ }
+
+ public static void callOnce() {
+ try {
+ Thread t = new ProxyMaker();
+ t.start();
+ t.join();
+ }
+ catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ System.out.println("** Done");
+ }
+
+ public static void main(String[] args) {
+ // callOnce();
+ ProxyFactory.useCache = args.length == 0;
+ TestRunner.run(suite());
+ }
+}
+
+@SuppressWarnings({"rawtypes","unused"})
+class ProxyMaker extends Thread implements MethodHandler {
+ private static final MethodFilter FINALIZE_FILTER = new MethodFilter() {
+ public boolean isHandled(Method m) {
+ // skip finalize methods
+ return !( m.getParameterTypes().length == 0 && m.getName().equals( "finalize" ) );
+ }
+ };
+
+ public void run() {
+ for (int i = 0; i < ProxyFactoryPerformanceTest.COUNT; ++i) {
+ callCreateClass();
+ }
+ }
+
+ public void callCreateClass() {
+ try {
+ ProxyFactory factory = new ProxyFactory();
+ factory.setSuperclass(SampleBean.class);
+ factory.setInterfaces(SampleBean.class.getInterfaces());
+ factory.setFilter(FINALIZE_FILTER);
+ // factory.setHandler(this);
+
+ Class proxyClass = factory.createClass();
+ //System.out.println("proxy name: " + proxyClass.getName());
+ } catch (Throwable e) {
+ e.printStackTrace();
+ ProxyFactoryPerformanceTest.error = e;
+ }
+ }
+
+ public Object invoke(Object arg0, Method arg1, Method arg2, Object[] arg3) throws Throwable {
+ return null;
+ }
+}
+
+/**
+class EnhancerUser extends Thread implements InvocationHandler {
+ private static final CallbackFilter FINALIZE_FILTER = new CallbackFilter() {
+ public int accept(Method method) {
+ if ( method.getParameterTypes().length == 0 && method.getName().equals("finalize") ){
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ };
+
+ public void run() {
+ for (int i = 0; i < ProxyFactoryPerformanceTest.COUNT; ++i) {
+ callCreateClass();
+ }
+ }
+
+ public void callCreateClass() {
+ try {
+ Enhancer enhancer = new Enhancer();
+ enhancer.setSuperclass(SampleBean.class);
+ enhancer.setInterfaces(SampleBean.class.getInterfaces());
+ enhancer.setCallbackTypes(new Class[] { InvocationHandler.class, NoOp.class });
+ enhancer.setCallbackFilter(FINALIZE_FILTER);
+ enhancer.setInterceptDuringConstruction(false);
+ // TODO
+ enhancer.setUseCache(false);
+ enhancer.setUseFactory(false);
+ Class proxyClass = enhancer.createClass();
+ //System.out.println("proxy name: " + proxyClass.getName());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
+ return null;
+ }
+}
+*/
+
+class SampleBean implements Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ long oid;
+
+ int version;
+
+ SampleBean bean;
+
+ public void setOid(long _oid) {
+ oid = _oid;
+ }
+
+ public long getOid() {
+ return oid;
+ }
+
+ public void setVersion(int _ver) {
+ version = _ver;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setBean(SampleBean _bean) {
+ bean = _bean;
+ }
+
+ public SampleBean getBean() {
+ return bean;
+ }
+}
diff --git a/src/test/testproxy/ProxyTester.java b/src/test/testproxy/ProxyTester.java
new file mode 100644
index 0000000..64712ef
--- /dev/null
+++ b/src/test/testproxy/ProxyTester.java
@@ -0,0 +1,477 @@
+package testproxy;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+
+import org.junit.Assert;
+
+import java.lang.reflect.InvocationTargetException;
+import javassist.util.proxy.ProxyFactory;
+import javassist.util.proxy.MethodFilter;
+import javassist.util.proxy.MethodHandler;
+import javassist.util.proxy.ProxyObject;
+import javassist.util.proxy.Proxy;
+import junit.framework.TestCase;
+import java.io.*;
+
+@SuppressWarnings({"unchecked", "rawtypes","unused"})
+public class ProxyTester extends TestCase {
+ public ProxyTester(String s) {
+ super(s);
+ }
+
+ public ProxyTester() {
+ this("proxy");
+ }
+
+ static class Interceptor1 implements MethodHandler {
+ int counter = 0;
+
+ public Object invoke(Object self, Method m, Method proceed,
+ Object[] args) throws Exception {
+ System.out.println("intercept: " + m + ", proceed: " + proceed);
+ System.out.println(" modifier: "
+ + Modifier.toString(proceed.getModifiers()));
+ counter++;
+ return proceed.invoke(self, args);
+ }
+ }
+
+ static class Interceptor2 implements MethodHandler {
+ int counter = 0;
+ public Object invoke(Object self, Method m, Method proceed,
+ Object[] args) throws Exception {
+ System.out.println("intercept: " + m + ", proceed: " + proceed);
+ counter++;
+ if (proceed != null)
+ return proceed.invoke(self, args);
+ else
+ if (m.getReturnType() == int.class)
+ return Integer.valueOf(3);
+ else
+ return "OK";
+ }
+ }
+
+ static MethodFilter finalizeRemover = new MethodFilter() {
+ public boolean isHandled(Method m) {
+ return !m.getName().equals("finalize");
+ }
+ };
+
+ public void testTarget() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.setSuperclass(Target.class);
+ Interceptor1 interceptor = new Interceptor1();
+ // f.setHandler(interceptor);
+ f.setFilter(finalizeRemover);
+ f.writeDirectory = ".";
+ Class c = f.createClass();
+ Target obj = (Target)c.getConstructor().newInstance();
+ ((Proxy)obj).setHandler(interceptor);
+ obj.m();
+ assertEquals(true, obj.m(true));
+ assertEquals((byte)1, obj.m1((byte)1));
+ assertEquals('a', obj.m2('a'));
+ assertEquals((short)2, obj.m3((short)2));
+ assertEquals(3, obj.m(3));
+ assertEquals(4L, obj.m5(4L));
+ assertTrue(5.0F == obj.m6(5.0F));
+ assertTrue(6.0 == obj.m7(6.0));
+ assertEquals("test", obj.m("test"));
+ int[] ia = { 1, 2, 3 };
+ assertEquals(ia, obj.m7(ia));
+ String[] sa = { "1", "2" };
+ assertEquals(sa, obj.m8(sa));
+ assertEquals(obj, obj.m9(3, obj, null));
+ assertEquals(14, interceptor.counter);
+ }
+
+ public void testTarget1() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.setSuperclass(Target1.class);
+ Interceptor1 interceptor = new Interceptor1();
+ // f.setHandler(interceptor);
+ f.setFilter(finalizeRemover);
+ Class c = f.createClass();
+ Target1 obj = (Target1)c.getConstructor().newInstance();
+ ((Proxy)obj).setHandler(interceptor);
+ assertEquals(null, obj.m(null));
+ assertEquals(1, interceptor.counter);
+ }
+
+ public void testObject() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ Interceptor1 interceptor = new Interceptor1();
+ // f.setHandler(interceptor);
+ f.setFilter(finalizeRemover);
+ Class c = f.createClass();
+ Object obj = (Object)c.getConstructor().newInstance();
+ ((Proxy)obj).setHandler(interceptor);
+ System.out.println(obj.toString());
+ assertEquals(2, interceptor.counter);
+ }
+
+ public void testSetter() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.writeDirectory = ".";
+ Interceptor1 interceptor = new Interceptor1();
+ // f.setHandler(interceptor);
+ f.setFilter(finalizeRemover);
+ Class c = f.createClass();
+ Object obj = (Object)c.getConstructor().newInstance();
+ ((Proxy)obj).setHandler(interceptor);
+ System.out.println("setter1: " + obj.toString());
+ ((ProxyObject)obj).setHandler(new MethodHandler() {
+ public Object invoke(Object self, Method m, Method proceed,
+ Object[] args) throws Exception {
+ System.out.print("intercept: " + m);
+ return "OK";
+ }
+ });
+ assertEquals("OK", obj.toString());
+ }
+
+ public void testString() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ Interceptor1 interceptor = new Interceptor1();
+ // f.setHandler(interceptor);
+ f.setFilter(finalizeRemover);
+ f.setSuperclass(String.class);
+ try {
+ Class c = f.createClass();
+ Assert.fail("String is final!");
+ }
+ catch (RuntimeException e) {
+ System.out.println(e);
+ }
+ }
+
+ public void testConstructor() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ Interceptor1 interceptor = new Interceptor1();
+ // f.setHandler(interceptor);
+ f.setFilter(finalizeRemover);
+ f.setSuperclass(Target2.class);
+ Class c = f.createClass();
+ Constructor[] cons = c.getDeclaredConstructors();
+ assertEquals(3, cons.length);
+ Constructor con1 = c.getDeclaredConstructor(new Class[] { int.class });
+ Constructor con2 = c.getDeclaredConstructor(new Class[] { int.class, int.class });
+ Method m1 = c.getDeclaredMethod("get", new Class[0]);
+ Method m2 = c.getDeclaredMethod("foo", new Class[0]);
+ assertEquals(0, m1.getExceptionTypes().length);
+ assertEquals("java.io.IOException", m2.getExceptionTypes()[0].getName());
+
+ Target2 t2 = (Target2)con1.newInstance(new Object[] { Integer.valueOf(1) });
+ ((Proxy)t2).setHandler(interceptor);
+ System.out.println(t2.toString());
+ assertEquals(2, interceptor.counter);
+
+ interceptor.counter = 0;
+ assertEquals(2, t2.foo());
+ assertEquals(4, t2._dfoo());
+ assertEquals(2, interceptor.counter);
+ }
+
+ public void testInterface() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ Interceptor2 interceptor2 = new Interceptor2();
+ // f.setHandler(interceptor2);
+ f.setFilter(finalizeRemover);
+ f.setInterfaces(new Class[] { Target3.class });
+ Class c = f.createClass();
+ Target3 obj = (Target3)c.getConstructor().newInstance();
+ ((Proxy)obj).setHandler(interceptor2);
+ assertEquals("OK", obj.m());
+ System.out.println(obj.toString());
+ assertEquals(3, interceptor2.counter);
+ }
+
+ public void test2Interfaces() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ Interceptor2 interceptor2 = new Interceptor2();
+ // f.setHandler(interceptor2);
+ f.setFilter(finalizeRemover);
+ f.setInterfaces(new Class[] { Target3.class, Target4.class });
+ Class c = f.createClass();
+ Target3 obj = (Target3)c.getConstructor().newInstance();
+ ((Proxy)obj).setHandler(interceptor2);
+ assertEquals("OK", obj.m());
+ System.out.println(obj.toString());
+ assertEquals(3, interceptor2.counter);
+
+ interceptor2.counter = 0;
+ Target4 obj4 = (Target4)c.getConstructor().newInstance();
+ ((Proxy)obj4).setHandler(interceptor2);
+ assertEquals(3, obj4.bar4());
+ assertEquals(3, obj4.foo4());
+ assertEquals(2, interceptor2.counter);
+ }
+
+ public void testFilter() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ Interceptor2 interceptor2 = new Interceptor2();
+ // f.setHandler(interceptor2);
+ f.setFilter(finalizeRemover);
+ f.setInterfaces(new Class[] { Target3.class });
+ f.setFilter(new MethodFilter() {
+ public boolean isHandled(Method m) {
+ return m.getDeclaringClass() != Object.class;
+ }
+ });
+ Class c = f.createClass();
+ Target3 obj = (Target3)c.getConstructor().newInstance();
+ ((Proxy)obj).setHandler(interceptor2);
+ assertEquals("OK", obj.m());
+ System.out.println(obj.toString());
+ assertEquals(1, interceptor2.counter);
+ }
+
+ public static boolean testInitFlag;
+
+ public void testInit() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.setSuperclass(TargetInit.class);
+ MethodHandler handler = new MethodHandler() {
+ public Object invoke(Object self, Method m,
+ Method proceed, Object[] args) throws Exception {
+ System.out.println("testInit " + testInitFlag);
+ return proceed.invoke(self, args);
+ }
+ };
+ testInitFlag = false;
+ Class c = f.createClass();
+ assertTrue(testInitFlag); // since 3.12. Before then, this line was assertFalse(testInitFlag);
+ System.out.println("testInit createClass(): " + testInitFlag);
+ TargetInit obj = (TargetInit)c.getConstructor().newInstance();
+ assertTrue(testInitFlag);
+ System.out.println("testInit newInstance(): " + testInitFlag);
+ ((ProxyObject)obj).setHandler(handler);
+ assertEquals("OK", obj.m());
+ }
+
+ public void testCreate() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.setSuperclass(Target5.class);
+ Interceptor1 interceptor = new Interceptor1();
+ // f.setHandler(interceptor);
+ f.setFilter(finalizeRemover);
+ Class c = f.createClass();
+ Target5 obj = (Target5)f.create(new Class[] { int.class }, new Object[] { Integer.valueOf(3) });
+ ((Proxy)obj).setHandler(interceptor);
+ assertEquals(3, obj.get());
+ }
+
+
+ public void testBridgeMethod() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ f.writeDirectory = ".";
+ f.setSuperclass(BridgeMethod.class);
+ Interceptor1 interceptor = new Interceptor1();
+ // f.setHandler(interceptor);
+ f.setFilter(finalizeRemover);
+ Class c = f.createClass();
+ BridgeMethod obj = (BridgeMethod)c.getConstructor().newInstance();
+ ((Proxy)obj).setHandler(interceptor);
+ Integer value = obj.m1();
+ assertEquals(7, value.intValue());
+ BridgeMethodInf inf = (BridgeMethodInf)obj;
+ Number num = inf.m1();
+ assertEquals(7, num.intValue());
+ BridgeMethodSuper sup = obj;
+ try {
+ Object x = sup.id(new Object());
+ fail("not cast error");
+ }
+ catch (ClassCastException e) {}
+ catch (Exception e) {
+ if (e instanceof InvocationTargetException)
+ if (e.getCause() instanceof ClassCastException)
+ return;
+
+ throw e;
+ }
+ }
+
+ public void testGetters() throws Exception {
+ ProxyFactory f = new ProxyFactory();
+ Class c = ProxyTester.class;
+ f.setSuperclass(c);
+ assertEquals(c, f.getSuperclass());
+ Class i = java.io.Serializable.class;
+ f.setInterfaces(new Class[] { i });
+ assertEquals(i, f.getInterfaces()[0]);
+ }
+
+ static class ProxyFactory2 extends ProxyFactory {
+ public ClassLoader getClassLoader2() {
+ return getClassLoader();
+ }
+ }
+
+ public void testProvider() throws Exception {
+ ProxyFactory.ClassLoaderProvider cp = ProxyFactory.classLoaderProvider;
+ try {
+ final ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
+ public ClassLoader get(ProxyFactory pf) {
+ return Thread.currentThread().getContextClassLoader();
+ }
+ };
+
+ ProxyFactory2 pf = new ProxyFactory2();
+ assertEquals(cl, pf.getClassLoader2());
+ }
+ finally {
+ ProxyFactory.classLoaderProvider = cp;
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testCache() throws Exception {
+ boolean prev = ProxyFactory.useCache;
+ ProxyFactory.useCache = true;
+ ProxyFactory f = new ProxyFactory();
+ f.setSuperclass(Cache1.class);
+ Class c = f.createClass();
+ ProxyFactory f2 = new ProxyFactory();
+ f2.setSuperclass(Cache1.class);
+ assertEquals(c, f2.createClass());
+ ProxyFactory f3 = new ProxyFactory();
+ f3.setSuperclass(Cache1.class);
+ f3.setHandler(new Interceptor1()); // deprecated
+ assertFalse(c == f3.createClass());
+ ProxyFactory.useCache = true;
+ ProxyFactory f4 = new ProxyFactory();
+ f4.setSuperclass(Cache1.class);
+ f4.setInterfaces(new Class[] { Cache2.class });
+ Class c4 = f4.createClass();
+ assertFalse(c == c4);
+ ProxyFactory f5 = new ProxyFactory();
+ f5.setSuperclass(Cache1.class);
+ f5.setInterfaces(new Class[] { Cache2.class });
+ assertEquals(c4, f5.createClass());
+ ProxyFactory f6 = new ProxyFactory();
+ f6.setInterfaces(new Class[] { Cache2.class });
+ assertFalse(c4 == f6.createClass());
+ ProxyFactory.useCache = prev;
+ }
+
+ public static class Cache1 {
+ public int foo() { return 0; }
+ }
+
+ public static interface Cache2 {
+ public int bar();
+ }
+
+ public void testReadWrite() throws Exception {
+ final String fileName = "read-write.bin";
+ ProxyFactory.ClassLoaderProvider cp = ProxyFactory.classLoaderProvider;
+ try {
+ ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
+ public ClassLoader get(ProxyFactory pf) {
+ /* If javassist.Loader is returned, the super type of ReadWriteData class,
+ * which is Serializable, is loaded by javassist.Loader as well as ReadWriteData.
+ * This breaks the implementation of the object serializer.
+ */
+ // return new javassist.Loader();
+ return Thread.currentThread().getContextClassLoader();
+ }
+ };
+ ProxyFactory pf = new ProxyFactory();
+ pf.setSuperclass(ReadWriteData.class);
+ Object data = pf.createClass().getConstructor().newInstance();
+ //Object data = new ReadWriteData();
+ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName));
+ oos.writeObject(data);
+ oos.close();
+ }
+ finally {
+ ProxyFactory.classLoaderProvider = cp;
+ }
+
+ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
+ Object data2 = ois.readObject();
+ ois.close();
+ int i = ((ReadWriteData)data2).foo();
+ assertEquals(4, i);
+ }
+
+ public static class ReadWriteData implements Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ public int foo() { return 4; }
+ }
+
+ public void testWriteReplace() throws Exception {
+ ProxyFactory pf = new ProxyFactory();
+ pf.setSuperclass(WriteReplace.class);
+ Object data = pf.createClass().getConstructor().newInstance();
+ assertEquals(data, ((WriteReplace)data).writeReplace());
+
+ ProxyFactory pf2 = new ProxyFactory();
+ pf2.setSuperclass(WriteReplace2.class);
+ Object data2 = pf2.createClass().getConstructor().newInstance();
+ Method meth = data2.getClass().getDeclaredMethod("writeReplace", new Class[0]);
+ assertEquals("javassist.util.proxy.SerializedProxy",
+ meth.invoke(data2, new Object[0]).getClass().getName());
+ }
+
+ public static class WriteReplace implements Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ public Object writeReplace() { return this; }
+ }
+
+ public static class WriteReplace2 implements Serializable {
+ /** default serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ public Object writeReplace(int i) { return Integer.valueOf(i); }
+ }
+
+ public static void testJIRA189() throws Exception {
+ Class persistentClass = Target189.PublishedArticle.class;
+ ProxyFactory factory = new ProxyFactory();
+ //factory.writeDirectory = ".";
+ factory.setUseCache(false);
+ factory.setSuperclass(persistentClass);
+ factory.setInterfaces(new Class[] { Target189.TestProxy.class });
+ Class cl = factory.createClass();
+ Object obj = cl.getConstructor().newInstance();
+ System.out.println("JIRA189:" + obj.getClass().getClassLoader() + ", " + obj.getClass().getSuperclass().getName()
+ + ", " + Target189.PublishedArticle.class.getClassLoader());
+ Target189.TestProxy proxy = (Target189.TestProxy)cl.getConstructor().newInstance();
+ Target189.TestMethodHandler methodHandler = new Target189.TestMethodHandler();
+ ((ProxyObject)proxy).setHandler(methodHandler);
+ ((Target189.Article)proxy).getIssue();
+ assertTrue(methodHandler.wasInvokedOnce());
+ methodHandler.reset();
+ Target189.PublishedArticle article = (Target189.PublishedArticle)proxy;
+ article.getIssue();
+ assertTrue(methodHandler.wasInvokedOnce());
+ }
+
+ public void testJIRA127() throws Exception {
+ ProxyFactory proxyFactory = new ProxyFactory();
+ // proxyFactory.writeDirectory = ".";
+ proxyFactory.setInterfaces(new Class[]{ Target127.Sub.class });
+ Target127.Sub proxy = (Target127.Sub)proxyFactory.create(new Class[0], new Object[0], new MethodHandler() {
+ public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
+ return null;
+ }
+ });
+ ((Target127.Super)proxy).item(); // proxyFactory must generate a bridge method.
+ ((Target127.Sub)proxy).item();
+ }
+
+ public static void main(String[] args) {
+ // javassist.bytecode.ClassFile.MAJOR_VERSION = javassist.bytecode.ClassFile.JAVA_6;
+ junit.textui.TestRunner.run(ProxyTester.class);
+ }
+}
diff --git a/src/test/testproxy/Target.java b/src/test/testproxy/Target.java
new file mode 100644
index 0000000..ab6789f
--- /dev/null
+++ b/src/test/testproxy/Target.java
@@ -0,0 +1,17 @@
+package testproxy;
+
+public class Target {
+ public void m() {}
+ public boolean m(boolean b) { return b; }
+ public byte m1(byte b) { return b; }
+ public char m2(char c) { return c; }
+ public short m3(short s) { return s; }
+ public int m(int i) { return i; }
+ public long m5(long j) { return j; }
+ public float m6(float f) { return f; }
+ public double m7(double d) { return d; }
+ public String m(String s) { return s; }
+ public int[] m7(int[] i) { return i; }
+ public String[] m8(String[] s) { return s; }
+ public Target m9(int i, Target t, Target[][] t2) { return t; }
+}
diff --git a/src/test/testproxy/Target1.java b/src/test/testproxy/Target1.java
new file mode 100644
index 0000000..ce972d0
--- /dev/null
+++ b/src/test/testproxy/Target1.java
@@ -0,0 +1,5 @@
+package testproxy;
+
+public class Target1 {
+ public Target1[][] m(Target1[][] a) { return a; }
+}
diff --git a/src/test/testproxy/Target127.java b/src/test/testproxy/Target127.java
new file mode 100644
index 0000000..aeb6fe2
--- /dev/null
+++ b/src/test/testproxy/Target127.java
@@ -0,0 +1,20 @@
+package testproxy;
+
+public class Target127 {
+ public interface Item { }
+ public interface CovariantItem extends Item { }
+
+ public interface Super {
+ Item item();
+ }
+
+ public interface Sub extends Super {
+ CovariantItem item();
+ }
+
+ public static class RealSub implements Sub {
+ public CovariantItem item() {
+ return null;
+ }
+ }
+}
diff --git a/src/test/testproxy/Target189.java b/src/test/testproxy/Target189.java
new file mode 100644
index 0000000..ebe7805
--- /dev/null
+++ b/src/test/testproxy/Target189.java
@@ -0,0 +1,74 @@
+package testproxy;
+
+import javassist.util.proxy.MethodHandler;
+import java.lang.reflect.Method;
+
+public class Target189 {
+ public interface TestProxy {
+ }
+
+ public static class TestMethodHandler implements MethodHandler {
+
+ int invoked = 0;
+
+ public Object invoke(Object self, Method thisMethod, Method proceed,
+ Object[] args) throws Throwable {
+ invoked++;
+ return proceed.invoke(self, args);
+ }
+
+ public boolean wasInvokedOnce() {
+ return invoked == 1;
+ }
+
+ public void reset() {
+ invoked = 0;
+ }
+ }
+
+ public static class Issue {
+
+ private Integer id;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+ }
+
+ public static class PublishedIssue extends Issue {
+ }
+
+ public static abstract class Article {
+ private Integer id;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public abstract Issue getIssue();
+ }
+
+ public static class PublishedArticle extends Article {
+
+ private PublishedIssue issue;
+
+ @Override
+ public PublishedIssue getIssue() {
+ return issue;
+ }
+
+ public void setIssue(PublishedIssue issue) {
+ this.issue = issue;
+ }
+
+ }
+
+}
diff --git a/src/test/testproxy/Target2.java b/src/test/testproxy/Target2.java
new file mode 100644
index 0000000..192bac7
--- /dev/null
+++ b/src/test/testproxy/Target2.java
@@ -0,0 +1,18 @@
+package testproxy;
+
+import java.io.IOException;
+
+@SuppressWarnings("unused")
+public class Target2 {
+ private int value;
+ public Target2(int i) { value = 1; }
+ protected Target2(int i, int j) { value = 2; }
+ private Target2(int i, double j) { value = 3; }
+ Target2(int i, long k) { value = 4; }
+
+ public int get() { return value; }
+ public int foo() throws IOException { return ++value; }
+ public int _dfoo() { value += 2; return value; }
+ private int _d100() { value += 3; return value; }
+ private int _d1003foo() { value += 3; return value; }
+}
diff --git a/src/test/testproxy/Target3.java b/src/test/testproxy/Target3.java
new file mode 100644
index 0000000..c9c8f66
--- /dev/null
+++ b/src/test/testproxy/Target3.java
@@ -0,0 +1,6 @@
+package testproxy;
+
+public interface Target3 {
+ String m();
+ String toString();
+}
diff --git a/src/test/testproxy/Target4.java b/src/test/testproxy/Target4.java
new file mode 100644
index 0000000..82ddef6
--- /dev/null
+++ b/src/test/testproxy/Target4.java
@@ -0,0 +1,9 @@
+package testproxy;
+
+interface Target4Super {
+ int foo4();
+}
+
+public interface Target4 extends Target4Super {
+ int bar4();
+}
diff --git a/src/test/testproxy/Target5.java b/src/test/testproxy/Target5.java
new file mode 100644
index 0000000..f40183a
--- /dev/null
+++ b/src/test/testproxy/Target5.java
@@ -0,0 +1,7 @@
+package testproxy;
+
+public class Target5 {
+ private int value;
+ public Target5(int i) { value = i; }
+ public int get() { return value; }
+}
diff --git a/src/test/testproxy/TargetInit.java b/src/test/testproxy/TargetInit.java
new file mode 100644
index 0000000..0654593
--- /dev/null
+++ b/src/test/testproxy/TargetInit.java
@@ -0,0 +1,10 @@
+package testproxy;
+
+public class TargetInit {
+ static {
+ System.out.println("TargetInit <clinit>");
+ ProxyTester.testInitFlag = true;
+ }
+
+ public String m() { return "OK"; }
+}
diff --git a/src/test/testproxy/sub/TargetSuper.java b/src/test/testproxy/sub/TargetSuper.java
new file mode 100644
index 0000000..9eb8d1d
--- /dev/null
+++ b/src/test/testproxy/sub/TargetSuper.java
@@ -0,0 +1,8 @@
+package testproxy.sub;
+
+@SuppressWarnings("unused")
+public class TargetSuper {
+ private int poi() { return 1; }
+ int poi2() { return 2; }
+ public int poi3() { return 3; }
+}
diff --git a/tutorial/tutorial.html b/tutorial/tutorial.html
index d62743d..b811039 100644
--- a/tutorial/tutorial.html
+++ b/tutorial/tutorial.html
@@ -26,6 +26,8 @@ Shigeru Chiba
<br>6. <a href="tutorial3.html#generics">Generics</a>
<br>7. <a href="tutorial3.html#varargs">Varargs</a>
<br>8. <a href="tutorial3.html#j2me">J2ME</a>
+<br>9. <a href="tutorial3.html#boxing">Boxing/Unboxing</a>
+<br>10. <a href="tutorial3.html#debug">Debug</a>
</ul>
<p><br>
@@ -37,7 +39,7 @@ Shigeru Chiba
Java bytecode is stored in a binary file called a class file.
Each class file contains one Java class or interface.
-<p>The class <code>Javassist.CtClass</code> is an absatract
+<p>The class <code>Javassist.CtClass</code> is an abstract
representation of a class file. A <code>CtClass</code> (compile-time
class) object is a handle for dealing with a class file. The
following program is a very simple example:
@@ -65,7 +67,7 @@ In the case of the program shown above, the
<code>test.Rectangle</code> is obtained from the
<code>ClassPool</code> object and it is assigned to a variable
<code>cc</code>.
-The <code>ClassPool</code> object returned by <code>getDfault()</code>
+The <code>ClassPool</code> object returned by <code>getDefault()</code>
searches the default system search path.
<p>From the implementation viewpoint, <code>ClassPool</code> is a hash
@@ -1044,7 +1046,9 @@ adds a new field <code>hiddenValue</code> to <code>java.lang.String</code>:
<ul><pre>ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("java.lang.String");
-cc.addField(new CtField(CtClass.intType, "hiddenValue", cc));
+CtField f = new CtField(CtClass.intType, "hiddenValue", cc);
+f.setModifiers(Modifier.PUBLIC);
+cc.addField(f);
cc.writeFile(".");</pre></ul>
<p>This program produces a file <code>"./java/lang/String.class"</code>.
@@ -1096,6 +1100,6 @@ For more information, see the API documentation of
<hr>
Java(TM) is a trademark of Sun Microsystems, Inc.<br>
-Copyright (C) 2000-2010 by Shigeru Chiba, All rights reserved.
+Copyright (C) 2000-2015 by Shigeru Chiba, All rights reserved.
</body>
</html>
diff --git a/tutorial/tutorial2.html b/tutorial/tutorial2.html
index 445fc90..f92f96e 100644
--- a/tutorial/tutorial2.html
+++ b/tutorial/tutorial2.html
@@ -481,7 +481,8 @@ compiled code at the end of the method. In the statement given to
available.
<p>The variable <code>$_</code> represents the resulting value of the
-method. The type of that variable is the type of the result type (the
+method.
+The type of that variable is the type of the result type (the
return type) of the method. If the result type is <code>void</code>,
then the type of <code>$_</code> is <code>Object</code> and the value
of <code>$_</code> is <code>null</code>.
@@ -1563,15 +1564,21 @@ has several limitations with respect to the language that the compiler can
accept. Those limitations are:
<p><li>The new syntax introduced by J2SE 5.0 (including enums and generics)
-has not been supported. Annotations are supported only by the low level
+has not been supported. Annotations are supported by the low level
API of Javassist.
-See the <code>javassist.bytecode.annotation</code> package.
+See the <code>javassist.bytecode.annotation</code> package
+(and also <code>getAnnotations()</code>
+in <code>CtClass</code> and <code>CtBehavior</code>).
+Generics are also only partly supported. See <a href="./tutorial3.html#generics">the latter section</a> for more details.
<p><li>Array initializers, a comma-separated list of expressions
enclosed by braces <code>{</code> and <code>}</code>, are not
available unless the array dimension is one.
<p><li>Inner classes or anonymous classes are not supported.
+Note that this is a limitation of the compiler only.
+It cannot compile source code including an anonymous-class declaration.
+Javassist can read and modify a class file of inner/anonymous class.
<p><li>Labeled <code>continue</code> and <code>break</code> statements
are not supported.
@@ -1622,6 +1629,6 @@ write:
<hr>
Java(TM) is a trademark of Sun Microsystems, Inc.<br>
-Copyright (C) 2000-2010 by Shigeru Chiba, All rights reserved.
+Copyright (C) 2000-2015 by Shigeru Chiba, All rights reserved.
</body>
</html>
diff --git a/tutorial/tutorial3.html b/tutorial/tutorial3.html
index dd4fb79..cb2a6b3 100644
--- a/tutorial/tutorial3.html
+++ b/tutorial/tutorial3.html
@@ -28,6 +28,10 @@
<p><a href="#j2me">8. J2ME</a>
+<p><a href="#boxing">9. Boxing/Unboxing
+
+<p><a href="#debug">10. Debug</a>
+
<p><br>
<a name="intro">
@@ -74,6 +78,28 @@ class file. <code>write()</code> in <code>ClassFile</code>
writes the contents of the class file to a given
<code>DataOutputStream</code>.
+<p>You can create a new class file from scratch. For example,
+<blockquote><pre>
+ClassFile cf = new ClassFile(false, "test.Foo", null);
+cf.setInterfaces(new String[] { "java.lang.Cloneable" });
+
+FieldInfo f = new FieldInfo(cf.getConstPool(), "width", "I");
+f.setAccessFlags(AccessFlag.PUBLIC);
+cf.addField(f);
+
+cf.write(new DataOutputStream(new FileOutputStream("Foo.class")));
+</pre></blockquote>
+
+<p>this code generates a class file <code>Foo.class</code> that contains
+the implementation of the following class:
+
+<blockquote><pre>
+package test;
+class Foo implements Cloneable {
+ public int width;
+}
+</pre></blockquote>
+
<p><br>
<a name="member">
@@ -215,6 +241,27 @@ constructed from the <code>Bytecode</code> object.
To recompute the maximum stack depth of a method body,
call <code>computeMaxStack()</code> in <code>CodeAttribute</code>.
+<p><code>Bytecode</code> can be used to construct a method.
+For example,
+
+<blockquote><pre>
+ClassFile cf = ...
+Bytecode code = new Bytecode(cf.getConstPool());
+code.addAload(0);
+code.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V");
+code.addReturn(null);
+code.setMaxLocals(1);
+
+MethodInfo minfo = new MethodInfo(cf.getConstPool(), MethodInfo.nameInit, "()V");
+minfo.setCodeAttribute(code.toCodeAttribute());
+cf.addMethod(minfo);
+</pre></blockquote>
+
+<p>this code makes the default constructor and adds it to the class specified
+by <code>cf</code>. The <code>Bytecode</code> object is first converted into
+a <code>CodeAttribute</code> object and then added to the method specified
+by <code>minfo</code>. The method is finally added to a class file <code>cf</code>.
+
<p><br>
<a name="annotation">
@@ -265,7 +312,15 @@ String s = (String)v.get(0);
</pre></ul>
<p>So when you write a bytecode transformer, you can just drop
-off all type parameters. For example, if you have a class:
+off all type parameters. Because the compiler embedded in Javassist
+does not support generics,
+you must insert an explicit type cast at the
+caller site if the source code is compiled by Javassist, for example,
+through <code>CtMethod.make()</code>. No type cast
+is necessary if the source code is compiled by a normal Java compiler
+such as <code>javac</code>.
+
+<p>For example, if you have a class:
<ul><pre>
public class Wrapper&lt;T&gt; {
@@ -283,7 +338,7 @@ public interface Getter&lt;T&gt; {
}
</pre></ul>
-<p>Then the interface you really have to add is <code>Getter</code>
+<p>then the interface you really have to add is <code>Getter</code>
(the type parameters <code>&lt;T&gt;</code> drops off)
and the method you also have to add to the <code>Wrapper</code>
class is this simple one:
@@ -293,6 +348,23 @@ public Object get() { return value; }
</pre></ul>
<p>Note that no type parameters are necessary.
+Since <code>get</code> returns an <code>Object</code>, an explicit type cast
+is needed at the caller site if the source code is compiled by Javassist.
+For example, if the type parameter <code>T</code>
+is <code>String</code>, then <code>(String)</code> must be inserted as follows:
+
+<ul><pre>
+Wrapper w = ...
+String s = (String)w.get();
+</pre></ul>
+
+<p>The type cast is not needed if the source code is compiled by a normal Java
+compiler because it will automatically insert a type cast.
+
+<p>If you need to make type parameters accessible through reflection
+during runtime, you have to add generic signatures to the class file.
+For more details, see the API documentation (javadoc) of the
+<code>setGenericSignature</code> method in the <code>CtClass</code>.
<p><br>
@@ -318,7 +390,8 @@ cc.addMethod(m);
<p>The parameter type <code>int...</code> is changed into <code>int[]</code>
and <code>Modifier.VARARGS</code> is added to the method modifiers.
-<p>To call this method, you must write:
+<p>To call this method in the source code compiled by the compiler embedded in Javassist,
+you must write:
<ul><pre>
length(new int[] { 1, 2, 3 });
@@ -357,10 +430,46 @@ objects, call the <code>getDeclaredMethods</code> method on a <code>CtClass</cod
<p><br>
+<h2><a name="boxing">9. Boxing/Unboxing</h2>
+
+<p>Boxing and unboxing in Java are syntactic sugar. There is no bytecode for
+boxing or unboxing. So the compiler of Javassist does not support them.
+For example, the following statement is valid in Java:
+
+<ul><pre>
+Integer i = 3;
+</pre></ul>
+
+<p>since boxing is implicitly performed. For Javassist, however, you must explicitly
+convert a value type from <code>int</code> to <code>Integer</code>:
+
+<ul><pre>
+Integer i = new Integer(3);
+</pre></ul>
+
+<p><br>
+
+<h2><a name="debug">10. Debug</h2>
+
+<p>Set <code>CtClass.debugDump</code> to a directory name.
+Then all class files modified and generated by Javassist are saved in that
+directory. To stop this, set <code>CtClass.debugDump</code> to null.
+The default value is null.
+
+<p>For example,
+
+<ul><pre>
+CtClass.debugDump = "./dump";
+</pre></ul>
+
+<p>All modified class files are saved in <code>./dump</code>.
+
+<p><br>
+
<a href="tutorial2.html">Previous page</a>
<hr>
Java(TM) is a trademark of Sun Microsystems, Inc.<br>
-Copyright (C) 2000-2010 by Shigeru Chiba, All rights reserved.
+Copyright (C) 2000-2015 by Shigeru Chiba, All rights reserved.
</body>
</html>