summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Boekenoogen <jboekeno@google.com>2014-06-02 18:56:43 -0700
committerJon Boekenoogen <jboekeno@google.com>2014-06-12 09:01:05 -0700
commit674060f01e9090cd21b3c5656cc3204912ad17a6 (patch)
treee009607bd55be60cff6bdd1e0e17535ea00b5394
parent6c4e9659e02223565e17c32063e24c6907795687 (diff)
downloadmockito-674060f01e9090cd21b3c5656cc3204912ad17a6.tar.gz
Update to support host use of mockito.
1) Download source necessary for host use. Sync'ed at the same CL 635c5f8353b0e49c4d3f2c71cc250a681c2e4cd6 the repo was currently at. 2) Remove host source from the target build. Should have no impact on pre-existing projects. 3) Get build.gradle working for target only. Will update to fix the host build later since it will require lots of dependent CLs. Change-Id: I31d49f422af2d5910cfd7acf86d6627ee3ee7fc2
-rw-r--r--Android.mk53
-rw-r--r--build.gradle13
-rw-r--r--cglib-and-asm/README.TXT8
-rw-r--r--cglib-and-asm/licenses/asm-license.txt29
-rw-r--r--cglib-and-asm/licenses/cglib-license.txt201
-rw-r--r--cglib-and-asm/src/org/mockito/asm/AnnotationVisitor.java97
-rw-r--r--cglib-and-asm/src/org/mockito/asm/AnnotationWriter.java316
-rw-r--r--cglib-and-asm/src/org/mockito/asm/Attribute.java254
-rw-r--r--cglib-and-asm/src/org/mockito/asm/ByteVector.java293
-rw-r--r--cglib-and-asm/src/org/mockito/asm/ClassAdapter.java121
-rw-r--r--cglib-and-asm/src/org/mockito/asm/ClassReader.java2009
-rw-r--r--cglib-and-asm/src/org/mockito/asm/ClassVisitor.java196
-rw-r--r--cglib-and-asm/src/org/mockito/asm/ClassWriter.java1344
-rw-r--r--cglib-and-asm/src/org/mockito/asm/Edge.java75
-rw-r--r--cglib-and-asm/src/org/mockito/asm/FieldVisitor.java64
-rw-r--r--cglib-and-asm/src/org/mockito/asm/FieldWriter.java269
-rw-r--r--cglib-and-asm/src/org/mockito/asm/Frame.java1402
-rw-r--r--cglib-and-asm/src/org/mockito/asm/Handler.java70
-rw-r--r--cglib-and-asm/src/org/mockito/asm/Item.java256
-rw-r--r--cglib-and-asm/src/org/mockito/asm/Label.java526
-rw-r--r--cglib-and-asm/src/org/mockito/asm/MethodAdapter.java195
-rw-r--r--cglib-and-asm/src/org/mockito/asm/MethodVisitor.java395
-rw-r--r--cglib-and-asm/src/org/mockito/asm/MethodWriter.java2601
-rw-r--r--cglib-and-asm/src/org/mockito/asm/Opcodes.java341
-rw-r--r--cglib-and-asm/src/org/mockito/asm/Type.java794
-rw-r--r--cglib-and-asm/src/org/mockito/asm/package.html87
-rw-r--r--cglib-and-asm/src/org/mockito/asm/signature/SignatureReader.java229
-rw-r--r--cglib-and-asm/src/org/mockito/asm/signature/SignatureVisitor.java185
-rw-r--r--cglib-and-asm/src/org/mockito/asm/signature/SignatureWriter.java207
-rw-r--r--cglib-and-asm/src/org/mockito/asm/signature/package.html36
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/AbstractInsnNode.java233
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/AnnotationNode.java191
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/ClassNode.java280
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/FieldInsnNode.java103
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/FieldNode.java127
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/FrameNode.java208
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/IincInsnNode.java77
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/InnerClassNode.java101
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/InsnList.java640
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/InsnNode.java81
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/IntInsnNode.java81
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/JumpInsnNode.java89
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/LabelNode.java75
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/LdcInsnNode.java74
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/LineNumberNode.java79
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/LocalVariableNode.java115
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/LookupSwitchInsnNode.java113
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/MemberNode.java120
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/MethodInsnNode.java104
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/MethodNode.java490
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/MultiANewArrayInsnNode.java78
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/TableSwitchInsnNode.java112
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/TryCatchBlockNode.java94
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/TypeInsnNode.java84
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/VarInsnNode.java87
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/Analyzer.java507
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/AnalyzerException.java56
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicInterpreter.java321
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicValue.java105
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicVerifier.java423
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/Frame.java667
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/Interpreter.java178
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/SimpleVerifier.java284
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/SmallSet.java126
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/SourceInterpreter.java174
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/SourceValue.java95
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/Subroutine.java93
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/Value.java45
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/analysis/package.html67
-rw-r--r--cglib-and-asm/src/org/mockito/asm/tree/package.html192
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/ASMifiable.java53
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/ASMifierAbstractVisitor.java222
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/ASMifierAnnotationVisitor.java127
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/ASMifierClassVisitor.java575
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/ASMifierFieldVisitor.java50
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/ASMifierMethodVisitor.java443
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/AbstractVisitor.java202
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/CheckAnnotationAdapter.java132
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/CheckClassAdapter.java480
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/CheckFieldAdapter.java77
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/CheckMethodAdapter.java1328
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/CheckSignatureAdapter.java290
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/TraceAbstractVisitor.java179
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/TraceAnnotationVisitor.java266
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/TraceClassVisitor.java523
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/TraceFieldVisitor.java78
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/TraceMethodVisitor.java567
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/TraceSignatureVisitor.java300
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/Traceable.java52
-rw-r--r--cglib-and-asm/src/org/mockito/asm/util/package.html40
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/beans/BeanCopier.java173
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/beans/BeanGenerator.java145
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/beans/BeanMap.java316
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/beans/BeanMapEmitter.java193
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/beans/BulkBean.java138
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/beans/BulkBeanEmitter.java157
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/beans/BulkBeanException.java43
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/beans/FixedKeySet.java36
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/beans/ImmutableBean.java125
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/AbstractClassGenerator.java244
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/Block.java49
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/ClassEmitter.java277
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/ClassGenerator.java22
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/ClassInfo.java47
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/ClassNameReader.java62
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/ClassesKey.java31
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/CodeEmitter.java864
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/CodeGenerationException.java32
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/CollectionUtils.java76
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/Constants.java67
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/Converter.java20
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/Customizer.java22
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/DebuggingClassWriter.java112
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/DefaultGeneratorStrategy.java40
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/DefaultNamingPolicy.java58
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/DuplicatesPredicate.java27
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/EmitUtils.java918
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/GeneratorStrategy.java44
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/KeyFactory.java261
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/Local.java37
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/LocalVariablesSorter.java159
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/MethodInfo.java47
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/MethodInfoTransformer.java37
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/MethodWrapper.java46
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/NamingPolicy.java43
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/ObjectSwitchCallback.java24
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/Predicate.java21
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/ProcessArrayCallback.java22
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/ProcessSwitchCallback.java23
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/ReflectUtils.java461
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/RejectModifierPredicate.java30
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/Signature.java73
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/TinyBitSet.java78
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/Transformer.java20
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/TypeUtils.java418
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/core/VisibilityPredicate.java44
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/Callback.java29
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/CallbackFilter.java45
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/CallbackGenerator.java36
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/CallbackHelper.java99
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/CallbackInfo.java103
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/Dispatcher.java30
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/DispatcherGenerator.java66
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/Enhancer.java1046
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/Factory.java79
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/FixedValue.java35
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/FixedValueGenerator.java43
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/InterfaceMaker.java119
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/InvocationHandler.java35
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/InvocationHandlerGenerator.java65
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/LazyLoader.java30
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/LazyLoaderGenerator.java89
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/MethodInterceptor.java42
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/MethodInterceptorGenerator.java240
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/MethodProxy.java221
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/Mixin.java238
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/MixinBeanEmitter.java39
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/MixinEmitter.java90
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/MixinEverythingEmitter.java50
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/NoOp.java28
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/NoOpGenerator.java44
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/Proxy.java102
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/ProxyRefDispatcher.java31
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/proxy/UndeclaredThrowableException.java36
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/reflect/ConstructorDelegate.java119
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/reflect/FastClass.java203
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/reflect/FastClassEmitter.java227
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/reflect/FastConstructor.java46
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/reflect/FastMember.java65
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/reflect/FastMethod.java59
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/reflect/MethodDelegate.java260
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/reflect/MulticastDelegate.java171
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassFilterTransformer.java85
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassLoader.java118
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassTransformer.java30
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/AbstractProcessTask.java64
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/AbstractTransformTask.java266
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/AnnotationVisitorTee.java59
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/ClassEmitterTransformer.java21
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/ClassFilter.java27
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/ClassFilterTransformer.java31
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/ClassReaderGenerator.java41
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformer.java22
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerChain.java56
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerFactory.java20
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerTee.java32
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/ClassVisitorTee.java97
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/FieldVisitorTee.java45
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/MethodFilter.java23
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/MethodFilterTransformer.java43
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/MethodVisitorTee.java151
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/TransformingClassGenerator.java35
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/TransformingClassLoader.java35
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/AbstractInterceptFieldCallback.java42
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/AccessFieldTransformer.java64
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/AddDelegateTransformer.java120
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/AddInitTransformer.java63
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/AddPropertyTransformer.java48
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/AddStaticInitTransformer.java50
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/FieldProvider.java34
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/FieldProviderTransformer.java209
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldCallback.java42
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldEnabled.java21
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldFilter.java23
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldTransformer.java214
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/UndeclaredThrowableStrategy.java53
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/transform/impl/UndeclaredThrowableTransformer.java61
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/util/ParallelSorter.java295
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/util/ParallelSorterEmitter.java101
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/util/SorterTemplate.java173
-rw-r--r--cglib-and-asm/src/org/mockito/cglib/util/StringSwitcher.java155
-rw-r--r--src/org/mockito/internal/creation/AbstractMockitoMethodProxy.java12
-rw-r--r--src/org/mockito/internal/creation/AcrossJVMSerializationFeature.java416
-rw-r--r--src/org/mockito/internal/creation/CglibMockMaker.java51
-rw-r--r--src/org/mockito/internal/creation/DelegatingMockitoMethodProxy.java20
-rw-r--r--src/org/mockito/internal/creation/MethodInterceptorFilter.java85
-rw-r--r--src/org/mockito/internal/creation/MockitoMethodProxy.java15
-rw-r--r--src/org/mockito/internal/creation/SerializableMockitoMethodProxy.java37
-rw-r--r--src/org/mockito/internal/creation/cglib/CGLIBHacker.java46
-rw-r--r--src/org/mockito/internal/creation/cglib/MockitoNamingPolicy.java17
-rw-r--r--src/org/mockito/internal/creation/cglib/package.html6
-rw-r--r--src/org/mockito/internal/creation/jmock/ClassImposterizer.java148
-rw-r--r--src/org/mockito/internal/creation/jmock/SearchingClassLoader.java76
-rw-r--r--src/org/mockito/internal/creation/jmock/SerializableNoOp.java20
-rw-r--r--src/org/mockito/internal/creation/jmock/jmock-license.txt25
-rw-r--r--src/org/mockito/internal/creation/jmock/package.html6
-rw-r--r--src/org/mockito/internal/invocation/realmethod/CGLIBProxyRealMethod.java28
-rw-r--r--src/org/mockito/internal/invocation/realmethod/FilteredCGLIBProxyRealMethod.java37
-rw-r--r--src/org/mockito/internal/invocation/realmethod/HasCGLIBMethodProxy.java14
-rwxr-xr-xupdate_source.sh22
230 files changed, 40396 insertions, 15 deletions
diff --git a/Android.mk b/Android.mk
index 788a229..a4a7e68 100644
--- a/Android.mk
+++ b/Android.mk
@@ -16,11 +16,55 @@
LOCAL_PATH := $(call my-dir)
+###################################################################
+# Host build
+###################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-java-files-under, cglib-and-asm/src)
+
+LOCAL_JAVA_LIBRARIES := junit objenesis-host ant
+LOCAL_MODULE := mockito-host
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+
+###################################################################
+# Target build
+###################################################################
+
# Builds the Mockito source code, but does not include any run-time
# dependencies. Most projects should use mockito-target instead, which includes
# everything needed to run Mockito tests.
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Exclude source used to dynamically create classes since target builds use
+# dexmaker instead and including it causes conflicts.
+explicit_target_excludes := \
+ src/org/mockito/internal/creation/AbstractMockitoMethodProxy.java \
+ src/org/mockito/internal/creation/AcrossJVMSerializationFeature.java \
+ src/org/mockito/internal/creation/CglibMockMaker.java \
+ src/org/mockito/internal/creation/DelegatingMockitoMethodProxy.java \
+ src/org/mockito/internal/creation/MethodInterceptorFilter.java \
+ src/org/mockito/internal/creation/MockitoMethodProxy.java \
+ src/org/mockito/internal/creation/SerializableMockitoMethodProxy.java \
+ src/org/mockito/internal/invocation/realmethod/FilteredCGLIBProxyRealMethod.java \
+ src/org/mockito/internal/invocation/realmethod/CGLIBProxyRealMethod.java \
+ src/org/mockito/internal/invocation/realmethod/HasCGLIBMethodProxy.java
+
+target_src_files := \
+ $(call all-java-files-under, src)
+target_src_files := \
+ $(filter-out src/org/mockito/internal/creation/cglib/%, $(target_src_files))
+target_src_files := \
+ $(filter-out src/org/mockito/internal/creation/jmock/%, $(target_src_files))
+target_src_files := \
+ $(filter-out $(explicit_target_excludes), $(target_src_files))
+
+LOCAL_SRC_FILES := $(target_src_files)
LOCAL_JAVA_LIBRARIES := junit4-target objenesis-target
LOCAL_MODULE := mockito-api
LOCAL_SDK_VERSION := 10
@@ -30,9 +74,16 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
# Main target for dependent projects. Bundles all the run-time dependencies
# needed to run Mockito tests on the device.
include $(CLEAR_VARS)
+
LOCAL_MODULE := mockito-target
LOCAL_STATIC_JAVA_LIBRARIES := mockito-api dexmaker dexmaker-mockmaker \
objenesis-target junit4-target
LOCAL_SDK_VERSION := 10
LOCAL_MODULE_TAGS := optional
include $(BUILD_STATIC_JAVA_LIBRARY)
+
+###################################################
+# Clean up temp vars
+###################################################
+explicit_target_excludes :=
+target_src_files :=
diff --git a/build.gradle b/build.gradle
index 47873ea..ebc889e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,9 +1,22 @@
apply plugin: 'java'
+// TODO(jboekeno): Create a host based build
sourceSets {
main {
java {
srcDirs = ['src']
+ exclude 'org/mockito/internal/creation/cglib/**',
+ 'org/mockito/internal/creation/jmock/**',
+ 'org/mockito/internal/creation/AbstractMockitoMethodProxy.java',
+ 'org/mockito/internal/creation/AcrossJVMSerializationFeature.java',
+ 'org/mockito/internal/creation/CglibMockMaker.java',
+ 'org/mockito/internal/creation/DelegatingMockitoMethodProxy.java',
+ 'org/mockito/internal/creation/MethodInterceptorFilter.java',
+ 'org/mockito/internal/creation/MockitoMethodProxy.java',
+ 'org/mockito/internal/creation/SerializableMockitoMethodProxy.java',
+ 'org/mockito/internal/invocation/realmethod/FilteredCGLIBProxyRealMethod.java',
+ 'org/mockito/internal/invocation/realmethod/CGLIBProxyRealMethod.java',
+ 'org/mockito/internal/invocation/realmethod/HasCGLIBMethodProxy.java'
}
}
}
diff --git a/cglib-and-asm/README.TXT b/cglib-and-asm/README.TXT
new file mode 100644
index 0000000..5a0092c
--- /dev/null
+++ b/cglib-and-asm/README.TXT
@@ -0,0 +1,8 @@
+This mockito subproject repackages cglib + asm to avoid problems with conflicting versions.
+
+Unfortunately, jarjar was not able to repackage sources
+therefore I'm using this subproject to repackage cglib+asm.
+We should NEVER modify cglib/asm - sources are here only for the sake of repackaging.
+
+Source was obtained from https://github.com/mockito/mockito/tree/master/cglib-and-asm and cglib+asm
+has been included in the Mockito builds since 2008 in Mockito 1.5.
diff --git a/cglib-and-asm/licenses/asm-license.txt b/cglib-and-asm/licenses/asm-license.txt
new file mode 100644
index 0000000..fe0bf7d
--- /dev/null
+++ b/cglib-and-asm/licenses/asm-license.txt
@@ -0,0 +1,29 @@
+Copyright (c) 2000-2005 INRIA, France Telecom
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holders nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/cglib-and-asm/licenses/cglib-license.txt b/cglib-and-asm/licenses/cglib-license.txt
new file mode 100644
index 0000000..f49a4e1
--- /dev/null
+++ b/cglib-and-asm/licenses/cglib-license.txt
@@ -0,0 +1,201 @@
+ 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. \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/AnnotationVisitor.java b/cglib-and-asm/src/org/mockito/asm/AnnotationVisitor.java
new file mode 100644
index 0000000..3a96c7d
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/AnnotationVisitor.java
@@ -0,0 +1,97 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * A visitor to visit a Java annotation. The methods of this interface must be
+ * called in the following order: (<tt>visit<tt> | <tt>visitEnum<tt> |
+ * <tt>visitAnnotation<tt> | <tt>visitArray<tt>)* <tt>visitEnd<tt>.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public interface AnnotationVisitor {
+
+ /**
+ * Visits a primitive value of the annotation.
+ *
+ * @param name the value name.
+ * @param value the actual value, whose type must be {@link Byte},
+ * {@link Boolean}, {@link Character}, {@link Short},
+ * {@link Integer}, {@link Long}, {@link Float}, {@link Double},
+ * {@link String} or {@link Type}. This value can also be an array
+ * of byte, boolean, short, char, int, long, float or double values
+ * (this is equivalent to using {@link #visitArray visitArray} and
+ * visiting each array element in turn, but is more convenient).
+ */
+ void visit(String name, Object value);
+
+ /**
+ * Visits an enumeration value of the annotation.
+ *
+ * @param name the value name.
+ * @param desc the class descriptor of the enumeration class.
+ * @param value the actual enumeration value.
+ */
+ void visitEnum(String name, String desc, String value);
+
+ /**
+ * Visits a nested annotation value of the annotation.
+ *
+ * @param name the value name.
+ * @param desc the class descriptor of the nested annotation class.
+ * @return a visitor to visit the actual nested annotation value, or
+ * <tt>null</tt> if this visitor is not interested in visiting
+ * this nested annotation. <i>The nested annotation value must be
+ * fully visited before calling other methods on this annotation
+ * visitor</i>.
+ */
+ AnnotationVisitor visitAnnotation(String name, String desc);
+
+ /**
+ * Visits an array value of the annotation. Note that arrays of primitive
+ * types (such as byte, boolean, short, char, int, long, float or double)
+ * can be passed as value to {@link #visit visit}. This is what
+ * {@link ClassReader} does.
+ *
+ * @param name the value name.
+ * @return a visitor to visit the actual array value elements, or
+ * <tt>null</tt> if this visitor is not interested in visiting
+ * these values. The 'name' parameters passed to the methods of this
+ * visitor are ignored. <i>All the array values must be visited
+ * before calling other methods on this annotation visitor</i>.
+ */
+ AnnotationVisitor visitArray(String name);
+
+ /**
+ * Visits the end of the annotation.
+ */
+ void visitEnd();
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/AnnotationWriter.java b/cglib-and-asm/src/org/mockito/asm/AnnotationWriter.java
new file mode 100644
index 0000000..1723f55
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/AnnotationWriter.java
@@ -0,0 +1,316 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * An {@link AnnotationVisitor} that generates annotations in bytecode form.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+final class AnnotationWriter implements AnnotationVisitor {
+
+ /**
+ * The class writer to which this annotation must be added.
+ */
+ private final ClassWriter cw;
+
+ /**
+ * The number of values in this annotation.
+ */
+ private int size;
+
+ /**
+ * <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation
+ * writers used for annotation default and annotation arrays use unnamed
+ * values.
+ */
+ private final boolean named;
+
+ /**
+ * The annotation values in bytecode form. This byte vector only contains
+ * the values themselves, i.e. the number of values must be stored as a
+ * unsigned short just before these bytes.
+ */
+ private final ByteVector bv;
+
+ /**
+ * The byte vector to be used to store the number of values of this
+ * annotation. See {@link #bv}.
+ */
+ private final ByteVector parent;
+
+ /**
+ * Where the number of values of this annotation must be stored in
+ * {@link #parent}.
+ */
+ private final int offset;
+
+ /**
+ * Next annotation writer. This field is used to store annotation lists.
+ */
+ AnnotationWriter next;
+
+ /**
+ * Previous annotation writer. This field is used to store annotation lists.
+ */
+ AnnotationWriter prev;
+
+ // ------------------------------------------------------------------------
+ // Constructor
+ // ------------------------------------------------------------------------
+
+ /**
+ * Constructs a new {@link AnnotationWriter}.
+ *
+ * @param cw the class writer to which this annotation must be added.
+ * @param named <tt>true<tt> if values are named, <tt>false</tt> otherwise.
+ * @param bv where the annotation values must be stored.
+ * @param parent where the number of annotation values must be stored.
+ * @param offset where in <tt>parent</tt> the number of annotation values must
+ * be stored.
+ */
+ AnnotationWriter(
+ final ClassWriter cw,
+ final boolean named,
+ final ByteVector bv,
+ final ByteVector parent,
+ final int offset)
+ {
+ this.cw = cw;
+ this.named = named;
+ this.bv = bv;
+ this.parent = parent;
+ this.offset = offset;
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the AnnotationVisitor interface
+ // ------------------------------------------------------------------------
+
+ public void visit(final String name, final Object value) {
+ ++size;
+ if (named) {
+ bv.putShort(cw.newUTF8(name));
+ }
+ if (value instanceof String) {
+ bv.put12('s', cw.newUTF8((String) value));
+ } else if (value instanceof Byte) {
+ bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index);
+ } else if (value instanceof Boolean) {
+ int v = ((Boolean) value).booleanValue() ? 1 : 0;
+ bv.put12('Z', cw.newInteger(v).index);
+ } else if (value instanceof Character) {
+ bv.put12('C', cw.newInteger(((Character) value).charValue()).index);
+ } else if (value instanceof Short) {
+ bv.put12('S', cw.newInteger(((Short) value).shortValue()).index);
+ } else if (value instanceof Type) {
+ bv.put12('c', cw.newUTF8(((Type) value).getDescriptor()));
+ } else if (value instanceof byte[]) {
+ byte[] v = (byte[]) value;
+ bv.put12('[', v.length);
+ for (int i = 0; i < v.length; i++) {
+ bv.put12('B', cw.newInteger(v[i]).index);
+ }
+ } else if (value instanceof boolean[]) {
+ boolean[] v = (boolean[]) value;
+ bv.put12('[', v.length);
+ for (int i = 0; i < v.length; i++) {
+ bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index);
+ }
+ } else if (value instanceof short[]) {
+ short[] v = (short[]) value;
+ bv.put12('[', v.length);
+ for (int i = 0; i < v.length; i++) {
+ bv.put12('S', cw.newInteger(v[i]).index);
+ }
+ } else if (value instanceof char[]) {
+ char[] v = (char[]) value;
+ bv.put12('[', v.length);
+ for (int i = 0; i < v.length; i++) {
+ bv.put12('C', cw.newInteger(v[i]).index);
+ }
+ } else if (value instanceof int[]) {
+ int[] v = (int[]) value;
+ bv.put12('[', v.length);
+ for (int i = 0; i < v.length; i++) {
+ bv.put12('I', cw.newInteger(v[i]).index);
+ }
+ } else if (value instanceof long[]) {
+ long[] v = (long[]) value;
+ bv.put12('[', v.length);
+ for (int i = 0; i < v.length; i++) {
+ bv.put12('J', cw.newLong(v[i]).index);
+ }
+ } else if (value instanceof float[]) {
+ float[] v = (float[]) value;
+ bv.put12('[', v.length);
+ for (int i = 0; i < v.length; i++) {
+ bv.put12('F', cw.newFloat(v[i]).index);
+ }
+ } else if (value instanceof double[]) {
+ double[] v = (double[]) value;
+ bv.put12('[', v.length);
+ for (int i = 0; i < v.length; i++) {
+ bv.put12('D', cw.newDouble(v[i]).index);
+ }
+ } else {
+ Item i = cw.newConstItem(value);
+ bv.put12(".s.IFJDCS".charAt(i.type), i.index);
+ }
+ }
+
+ public void visitEnum(
+ final String name,
+ final String desc,
+ final String value)
+ {
+ ++size;
+ if (named) {
+ bv.putShort(cw.newUTF8(name));
+ }
+ bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value));
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String name,
+ final String desc)
+ {
+ ++size;
+ if (named) {
+ bv.putShort(cw.newUTF8(name));
+ }
+ // write tag and type, and reserve space for values count
+ bv.put12('@', cw.newUTF8(desc)).putShort(0);
+ return new AnnotationWriter(cw, true, bv, bv, bv.length - 2);
+ }
+
+ public AnnotationVisitor visitArray(final String name) {
+ ++size;
+ if (named) {
+ bv.putShort(cw.newUTF8(name));
+ }
+ // write tag, and reserve space for array size
+ bv.put12('[', 0);
+ return new AnnotationWriter(cw, false, bv, bv, bv.length - 2);
+ }
+
+ public void visitEnd() {
+ if (parent != null) {
+ byte[] data = parent.data;
+ data[offset] = (byte) (size >>> 8);
+ data[offset + 1] = (byte) size;
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the size of this annotation writer list.
+ *
+ * @return the size of this annotation writer list.
+ */
+ int getSize() {
+ int size = 0;
+ AnnotationWriter aw = this;
+ while (aw != null) {
+ size += aw.bv.length;
+ aw = aw.next;
+ }
+ return size;
+ }
+
+ /**
+ * Puts the annotations of this annotation writer list into the given byte
+ * vector.
+ *
+ * @param out where the annotations must be put.
+ */
+ void put(final ByteVector out) {
+ int n = 0;
+ int size = 2;
+ AnnotationWriter aw = this;
+ AnnotationWriter last = null;
+ while (aw != null) {
+ ++n;
+ size += aw.bv.length;
+ aw.visitEnd(); // in case user forgot to call visitEnd
+ aw.prev = last;
+ last = aw;
+ aw = aw.next;
+ }
+ out.putInt(size);
+ out.putShort(n);
+ aw = last;
+ while (aw != null) {
+ out.putByteArray(aw.bv.data, 0, aw.bv.length);
+ aw = aw.prev;
+ }
+ }
+
+ /**
+ * Puts the given annotation lists into the given byte vector.
+ *
+ * @param panns an array of annotation writer lists.
+ * @param off index of the first annotation to be written.
+ * @param out where the annotations must be put.
+ */
+ static void put(
+ final AnnotationWriter[] panns,
+ final int off,
+ final ByteVector out)
+ {
+ int size = 1 + 2 * (panns.length - off);
+ for (int i = off; i < panns.length; ++i) {
+ size += panns[i] == null ? 0 : panns[i].getSize();
+ }
+ out.putInt(size).putByte(panns.length - off);
+ for (int i = off; i < panns.length; ++i) {
+ AnnotationWriter aw = panns[i];
+ AnnotationWriter last = null;
+ int n = 0;
+ while (aw != null) {
+ ++n;
+ aw.visitEnd(); // in case user forgot to call visitEnd
+ aw.prev = last;
+ last = aw;
+ aw = aw.next;
+ }
+ out.putShort(n);
+ aw = last;
+ while (aw != null) {
+ out.putByteArray(aw.bv.data, 0, aw.bv.length);
+ aw = aw.prev;
+ }
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/Attribute.java b/cglib-and-asm/src/org/mockito/asm/Attribute.java
new file mode 100644
index 0000000..3cea49a
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/Attribute.java
@@ -0,0 +1,254 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * A non standard class, field, method or code attribute.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class Attribute {
+
+ /**
+ * The type of this attribute.
+ */
+ public final String type;
+
+ /**
+ * The raw value of this attribute, used only for unknown attributes.
+ */
+ byte[] value;
+
+ /**
+ * The next attribute in this attribute list. May be <tt>null</tt>.
+ */
+ Attribute next;
+
+ /**
+ * Constructs a new empty attribute.
+ *
+ * @param type the type of the attribute.
+ */
+ protected Attribute(final String type) {
+ this.type = type;
+ }
+
+ /**
+ * Returns <tt>true</tt> if this type of attribute is unknown. The default
+ * implementation of this method always returns <tt>true</tt>.
+ *
+ * @return <tt>true</tt> if this type of attribute is unknown.
+ */
+ public boolean isUnknown() {
+ return true;
+ }
+
+ /**
+ * Returns <tt>true</tt> if this type of attribute is a code attribute.
+ *
+ * @return <tt>true</tt> if this type of attribute is a code attribute.
+ */
+ public boolean isCodeAttribute() {
+ return false;
+ }
+
+ /**
+ * Returns the labels corresponding to this attribute.
+ *
+ * @return the labels corresponding to this attribute, or <tt>null</tt> if
+ * this attribute is not a code attribute that contains labels.
+ */
+ protected Label[] getLabels() {
+ return null;
+ }
+
+ /**
+ * Reads a {@link #type type} attribute. This method must return a <i>new</i>
+ * {@link Attribute} object, of type {@link #type type}, corresponding to
+ * the <tt>len</tt> bytes starting at the given offset, in the given class
+ * reader.
+ *
+ * @param cr the class that contains the attribute to be read.
+ * @param off index of the first byte of the attribute's content in {@link
+ * ClassReader#b cr.b}. The 6 attribute header bytes, containing the
+ * type and the length of the attribute, are not taken into account
+ * here.
+ * @param len the length of the attribute's content.
+ * @param buf buffer to be used to call
+ * {@link ClassReader#readUTF8 readUTF8},
+ * {@link ClassReader#readClass(int,char[]) readClass} or
+ * {@link ClassReader#readConst readConst}.
+ * @param codeOff index of the first byte of code's attribute content in
+ * {@link ClassReader#b cr.b}, or -1 if the attribute to be read is
+ * not a code attribute. The 6 attribute header bytes, containing the
+ * type and the length of the attribute, are not taken into account
+ * here.
+ * @param labels the labels of the method's code, or <tt>null</tt> if the
+ * attribute to be read is not a code attribute.
+ * @return a <i>new</i> {@link Attribute} object corresponding to the given
+ * bytes.
+ */
+ protected Attribute read(
+ final ClassReader cr,
+ final int off,
+ final int len,
+ final char[] buf,
+ final int codeOff,
+ final Label[] labels)
+ {
+ Attribute attr = new Attribute(type);
+ attr.value = new byte[len];
+ System.arraycopy(cr.b, off, attr.value, 0, len);
+ return attr;
+ }
+
+ /**
+ * Returns the byte array form of this attribute.
+ *
+ * @param cw the class to which this attribute must be added. This parameter
+ * can be used to add to the constant pool of this class the items
+ * that corresponds to this attribute.
+ * @param code the bytecode of the method corresponding to this code
+ * attribute, or <tt>null</tt> if this attribute is not a code
+ * attributes.
+ * @param len the length of the bytecode of the method corresponding to this
+ * code attribute, or <tt>null</tt> if this attribute is not a code
+ * attribute.
+ * @param maxStack the maximum stack size of the method corresponding to
+ * this code attribute, or -1 if this attribute is not a code
+ * attribute.
+ * @param maxLocals the maximum number of local variables of the method
+ * corresponding to this code attribute, or -1 if this attribute is
+ * not a code attribute.
+ * @return the byte array form of this attribute.
+ */
+ protected ByteVector write(
+ final ClassWriter cw,
+ final byte[] code,
+ final int len,
+ final int maxStack,
+ final int maxLocals)
+ {
+ ByteVector v = new ByteVector();
+ v.data = value;
+ v.length = value.length;
+ return v;
+ }
+
+ /**
+ * Returns the length of the attribute list that begins with this attribute.
+ *
+ * @return the length of the attribute list that begins with this attribute.
+ */
+ final int getCount() {
+ int count = 0;
+ Attribute attr = this;
+ while (attr != null) {
+ count += 1;
+ attr = attr.next;
+ }
+ return count;
+ }
+
+ /**
+ * Returns the size of all the attributes in this attribute list.
+ *
+ * @param cw the class writer to be used to convert the attributes into byte
+ * arrays, with the {@link #write write} method.
+ * @param code the bytecode of the method corresponding to these code
+ * attributes, or <tt>null</tt> if these attributes are not code
+ * attributes.
+ * @param len the length of the bytecode of the method corresponding to
+ * these code attributes, or <tt>null</tt> if these attributes are
+ * not code attributes.
+ * @param maxStack the maximum stack size of the method corresponding to
+ * these code attributes, or -1 if these attributes are not code
+ * attributes.
+ * @param maxLocals the maximum number of local variables of the method
+ * corresponding to these code attributes, or -1 if these attributes
+ * are not code attributes.
+ * @return the size of all the attributes in this attribute list. This size
+ * includes the size of the attribute headers.
+ */
+ final int getSize(
+ final ClassWriter cw,
+ final byte[] code,
+ final int len,
+ final int maxStack,
+ final int maxLocals)
+ {
+ Attribute attr = this;
+ int size = 0;
+ while (attr != null) {
+ cw.newUTF8(attr.type);
+ size += attr.write(cw, code, len, maxStack, maxLocals).length + 6;
+ attr = attr.next;
+ }
+ return size;
+ }
+
+ /**
+ * Writes all the attributes of this attribute list in the given byte
+ * vector.
+ *
+ * @param cw the class writer to be used to convert the attributes into byte
+ * arrays, with the {@link #write write} method.
+ * @param code the bytecode of the method corresponding to these code
+ * attributes, or <tt>null</tt> if these attributes are not code
+ * attributes.
+ * @param len the length of the bytecode of the method corresponding to
+ * these code attributes, or <tt>null</tt> if these attributes are
+ * not code attributes.
+ * @param maxStack the maximum stack size of the method corresponding to
+ * these code attributes, or -1 if these attributes are not code
+ * attributes.
+ * @param maxLocals the maximum number of local variables of the method
+ * corresponding to these code attributes, or -1 if these attributes
+ * are not code attributes.
+ * @param out where the attributes must be written.
+ */
+ final void put(
+ final ClassWriter cw,
+ final byte[] code,
+ final int len,
+ final int maxStack,
+ final int maxLocals,
+ final ByteVector out)
+ {
+ Attribute attr = this;
+ while (attr != null) {
+ ByteVector b = attr.write(cw, code, len, maxStack, maxLocals);
+ out.putShort(cw.newUTF8(attr.type)).putInt(b.length);
+ out.putByteArray(b.data, 0, b.length);
+ attr = attr.next;
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/ByteVector.java b/cglib-and-asm/src/org/mockito/asm/ByteVector.java
new file mode 100644
index 0000000..3d8ada5
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/ByteVector.java
@@ -0,0 +1,293 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * A dynamically extensible vector of bytes. This class is roughly equivalent to
+ * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient.
+ *
+ * @author Eric Bruneton
+ */
+public class ByteVector {
+
+ /**
+ * The content of this vector.
+ */
+ byte[] data;
+
+ /**
+ * Actual number of bytes in this vector.
+ */
+ int length;
+
+ /**
+ * Constructs a new {@link ByteVector ByteVector} with a default initial
+ * size.
+ */
+ public ByteVector() {
+ data = new byte[64];
+ }
+
+ /**
+ * Constructs a new {@link ByteVector ByteVector} with the given initial
+ * size.
+ *
+ * @param initialSize the initial size of the byte vector to be constructed.
+ */
+ public ByteVector(final int initialSize) {
+ data = new byte[initialSize];
+ }
+
+ /**
+ * Puts a byte into this byte vector. The byte vector is automatically
+ * enlarged if necessary.
+ *
+ * @param b a byte.
+ * @return this byte vector.
+ */
+ public ByteVector putByte(final int b) {
+ int length = this.length;
+ if (length + 1 > data.length) {
+ enlarge(1);
+ }
+ data[length++] = (byte) b;
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * Puts two bytes into this byte vector. The byte vector is automatically
+ * enlarged if necessary.
+ *
+ * @param b1 a byte.
+ * @param b2 another byte.
+ * @return this byte vector.
+ */
+ ByteVector put11(final int b1, final int b2) {
+ int length = this.length;
+ if (length + 2 > data.length) {
+ enlarge(2);
+ }
+ byte[] data = this.data;
+ data[length++] = (byte) b1;
+ data[length++] = (byte) b2;
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * Puts a short into this byte vector. The byte vector is automatically
+ * enlarged if necessary.
+ *
+ * @param s a short.
+ * @return this byte vector.
+ */
+ public ByteVector putShort(final int s) {
+ int length = this.length;
+ if (length + 2 > data.length) {
+ enlarge(2);
+ }
+ byte[] data = this.data;
+ data[length++] = (byte) (s >>> 8);
+ data[length++] = (byte) s;
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * Puts a byte and a short into this byte vector. The byte vector is
+ * automatically enlarged if necessary.
+ *
+ * @param b a byte.
+ * @param s a short.
+ * @return this byte vector.
+ */
+ ByteVector put12(final int b, final int s) {
+ int length = this.length;
+ if (length + 3 > data.length) {
+ enlarge(3);
+ }
+ byte[] data = this.data;
+ data[length++] = (byte) b;
+ data[length++] = (byte) (s >>> 8);
+ data[length++] = (byte) s;
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * Puts an int into this byte vector. The byte vector is automatically
+ * enlarged if necessary.
+ *
+ * @param i an int.
+ * @return this byte vector.
+ */
+ public ByteVector putInt(final int i) {
+ int length = this.length;
+ if (length + 4 > data.length) {
+ enlarge(4);
+ }
+ byte[] data = this.data;
+ data[length++] = (byte) (i >>> 24);
+ data[length++] = (byte) (i >>> 16);
+ data[length++] = (byte) (i >>> 8);
+ data[length++] = (byte) i;
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * Puts a long into this byte vector. The byte vector is automatically
+ * enlarged if necessary.
+ *
+ * @param l a long.
+ * @return this byte vector.
+ */
+ public ByteVector putLong(final long l) {
+ int length = this.length;
+ if (length + 8 > data.length) {
+ enlarge(8);
+ }
+ byte[] data = this.data;
+ int i = (int) (l >>> 32);
+ data[length++] = (byte) (i >>> 24);
+ data[length++] = (byte) (i >>> 16);
+ data[length++] = (byte) (i >>> 8);
+ data[length++] = (byte) i;
+ i = (int) l;
+ data[length++] = (byte) (i >>> 24);
+ data[length++] = (byte) (i >>> 16);
+ data[length++] = (byte) (i >>> 8);
+ data[length++] = (byte) i;
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * Puts an UTF8 string into this byte vector. The byte vector is
+ * automatically enlarged if necessary.
+ *
+ * @param s a String.
+ * @return this byte vector.
+ */
+ public ByteVector putUTF8(final String s) {
+ int charLength = s.length();
+ if (length + 2 + charLength > data.length) {
+ enlarge(2 + charLength);
+ }
+ int len = length;
+ byte[] data = this.data;
+ // optimistic algorithm: instead of computing the byte length and then
+ // serializing the string (which requires two loops), we assume the byte
+ // length is equal to char length (which is the most frequent case), and
+ // we start serializing the string right away. During the serialization,
+ // if we find that this assumption is wrong, we continue with the
+ // general method.
+ data[len++] = (byte) (charLength >>> 8);
+ data[len++] = (byte) charLength;
+ for (int i = 0; i < charLength; ++i) {
+ char c = s.charAt(i);
+ if (c >= '\001' && c <= '\177') {
+ data[len++] = (byte) c;
+ } else {
+ int byteLength = i;
+ for (int j = i; j < charLength; ++j) {
+ c = s.charAt(j);
+ if (c >= '\001' && c <= '\177') {
+ byteLength++;
+ } else if (c > '\u07FF') {
+ byteLength += 3;
+ } else {
+ byteLength += 2;
+ }
+ }
+ data[length] = (byte) (byteLength >>> 8);
+ data[length + 1] = (byte) byteLength;
+ if (length + 2 + byteLength > data.length) {
+ length = len;
+ enlarge(2 + byteLength);
+ data = this.data;
+ }
+ for (int j = i; j < charLength; ++j) {
+ c = s.charAt(j);
+ if (c >= '\001' && c <= '\177') {
+ data[len++] = (byte) c;
+ } else if (c > '\u07FF') {
+ data[len++] = (byte) (0xE0 | c >> 12 & 0xF);
+ data[len++] = (byte) (0x80 | c >> 6 & 0x3F);
+ data[len++] = (byte) (0x80 | c & 0x3F);
+ } else {
+ data[len++] = (byte) (0xC0 | c >> 6 & 0x1F);
+ data[len++] = (byte) (0x80 | c & 0x3F);
+ }
+ }
+ break;
+ }
+ }
+ length = len;
+ return this;
+ }
+
+ /**
+ * Puts an array of bytes into this byte vector. The byte vector is
+ * automatically enlarged if necessary.
+ *
+ * @param b an array of bytes. May be <tt>null</tt> to put <tt>len</tt>
+ * null bytes into this byte vector.
+ * @param off index of the fist byte of b that must be copied.
+ * @param len number of bytes of b that must be copied.
+ * @return this byte vector.
+ */
+ public ByteVector putByteArray(final byte[] b, final int off, final int len)
+ {
+ if (length + len > data.length) {
+ enlarge(len);
+ }
+ if (b != null) {
+ System.arraycopy(b, off, data, length, len);
+ }
+ length += len;
+ return this;
+ }
+
+ /**
+ * Enlarge this byte vector so that it can receive n more bytes.
+ *
+ * @param size number of additional bytes that this byte vector should be
+ * able to receive.
+ */
+ private void enlarge(final int size) {
+ int length1 = 2 * data.length;
+ int length2 = length + size;
+ byte[] newData = new byte[length1 > length2 ? length1 : length2];
+ System.arraycopy(data, 0, newData, 0, length);
+ data = newData;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/ClassAdapter.java b/cglib-and-asm/src/org/mockito/asm/ClassAdapter.java
new file mode 100644
index 0000000..8e88dbc
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/ClassAdapter.java
@@ -0,0 +1,121 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * An empty {@link ClassVisitor} that delegates to another {@link ClassVisitor}.
+ * This class can be used as a super class to quickly implement usefull class
+ * adapter classes, just by overriding the necessary methods.
+ *
+ * @author Eric Bruneton
+ */
+public class ClassAdapter implements ClassVisitor {
+
+ /**
+ * The {@link ClassVisitor} to which this adapter delegates calls.
+ */
+ protected ClassVisitor cv;
+
+ /**
+ * Constructs a new {@link ClassAdapter} object.
+ *
+ * @param cv the class visitor to which this adapter must delegate calls.
+ */
+ public ClassAdapter(final ClassVisitor cv) {
+ this.cv = cv;
+ }
+
+ public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces)
+ {
+ cv.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ public void visitSource(final String source, final String debug) {
+ cv.visitSource(source, debug);
+ }
+
+ public void visitOuterClass(
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ cv.visitOuterClass(owner, name, desc);
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ return cv.visitAnnotation(desc, visible);
+ }
+
+ public void visitAttribute(final Attribute attr) {
+ cv.visitAttribute(attr);
+ }
+
+ public void visitInnerClass(
+ final String name,
+ final String outerName,
+ final String innerName,
+ final int access)
+ {
+ cv.visitInnerClass(name, outerName, innerName, access);
+ }
+
+ public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value)
+ {
+ return cv.visitField(access, name, desc, signature, value);
+ }
+
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions)
+ {
+ return cv.visitMethod(access, name, desc, signature, exceptions);
+ }
+
+ public void visitEnd() {
+ cv.visitEnd();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/ClassReader.java b/cglib-and-asm/src/org/mockito/asm/ClassReader.java
new file mode 100644
index 0000000..decb377
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/ClassReader.java
@@ -0,0 +1,2009 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * A Java class parser to make a {@link ClassVisitor} visit an existing class.
+ * This class parses a byte array conforming to the Java class file format and
+ * calls the appropriate visit methods of a given class visitor for each field,
+ * method and bytecode instruction encountered.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class ClassReader {
+
+ /**
+ * True to enable signatures support.
+ */
+ static final boolean SIGNATURES = true;
+
+ /**
+ * True to enable annotations support.
+ */
+ static final boolean ANNOTATIONS = true;
+
+ /**
+ * True to enable stack map frames support.
+ */
+ static final boolean FRAMES = true;
+
+ /**
+ * True to enable bytecode writing support.
+ */
+ static final boolean WRITER = true;
+
+ /**
+ * True to enable JSR_W and GOTO_W support.
+ */
+ static final boolean RESIZE = true;
+
+ /**
+ * Flag to skip method code. If this class is set <code>CODE</code>
+ * attribute won't be visited. This can be used, for example, to retrieve
+ * annotations for methods and method parameters.
+ */
+ public static final int SKIP_CODE = 1;
+
+ /**
+ * Flag to skip the debug information in the class. If this flag is set the
+ * debug information of the class is not visited, i.e. the
+ * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and
+ * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be
+ * called.
+ */
+ public static final int SKIP_DEBUG = 2;
+
+ /**
+ * Flag to skip the stack map frames in the class. If this flag is set the
+ * stack map frames of the class is not visited, i.e. the
+ * {@link MethodVisitor#visitFrame visitFrame} method will not be called.
+ * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is
+ * used: it avoids visiting frames that will be ignored and recomputed from
+ * scratch in the class writer.
+ */
+ public static final int SKIP_FRAMES = 4;
+
+ /**
+ * Flag to expand the stack map frames. By default stack map frames are
+ * visited in their original format (i.e. "expanded" for classes whose
+ * version is less than V1_6, and "compressed" for the other classes). If
+ * this flag is set, stack map frames are always visited in expanded format
+ * (this option adds a decompression/recompression step in ClassReader and
+ * ClassWriter which degrades performances quite a lot).
+ */
+ public static final int EXPAND_FRAMES = 8;
+
+ /**
+ * The class to be parsed. <i>The content of this array must not be
+ * modified. This field is intended for {@link Attribute} sub classes, and
+ * is normally not needed by class generators or adapters.</i>
+ */
+ public final byte[] b;
+
+ /**
+ * The start index of each constant pool item in {@link #b b}, plus one.
+ * The one byte offset skips the constant pool item tag that indicates its
+ * type.
+ */
+ private final int[] items;
+
+ /**
+ * The String objects corresponding to the CONSTANT_Utf8 items. This cache
+ * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item,
+ * which GREATLY improves performances (by a factor 2 to 3). This caching
+ * strategy could be extended to all constant pool items, but its benefit
+ * would not be so great for these items (because they are much less
+ * expensive to parse than CONSTANT_Utf8 items).
+ */
+ private final String[] strings;
+
+ /**
+ * Maximum length of the strings contained in the constant pool of the
+ * class.
+ */
+ private final int maxStringLength;
+
+ /**
+ * Start index of the class header information (access, name...) in
+ * {@link #b b}.
+ */
+ public final int header;
+
+ // ------------------------------------------------------------------------
+ // Constructors
+ // ------------------------------------------------------------------------
+
+ /**
+ * Constructs a new {@link ClassReader} object.
+ *
+ * @param b the bytecode of the class to be read.
+ */
+ public ClassReader(final byte[] b) {
+ this(b, 0, b.length);
+ }
+
+ /**
+ * Constructs a new {@link ClassReader} object.
+ *
+ * @param b the bytecode of the class to be read.
+ * @param off the start offset of the class data.
+ * @param len the length of the class data.
+ */
+ public ClassReader(final byte[] b, final int off, final int len) {
+ this.b = b;
+ // parses the constant pool
+ items = new int[readUnsignedShort(off + 8)];
+ int n = items.length;
+ strings = new String[n];
+ int max = 0;
+ int index = off + 10;
+ for (int i = 1; i < n; ++i) {
+ items[i] = index + 1;
+ int size;
+ switch (b[index]) {
+ case ClassWriter.FIELD:
+ case ClassWriter.METH:
+ case ClassWriter.IMETH:
+ case ClassWriter.INT:
+ case ClassWriter.FLOAT:
+ case ClassWriter.NAME_TYPE:
+ size = 5;
+ break;
+ case ClassWriter.LONG:
+ case ClassWriter.DOUBLE:
+ size = 9;
+ ++i;
+ break;
+ case ClassWriter.UTF8:
+ size = 3 + readUnsignedShort(index + 1);
+ if (size > max) {
+ max = size;
+ }
+ break;
+ // case ClassWriter.CLASS:
+ // case ClassWriter.STR:
+ default:
+ size = 3;
+ break;
+ }
+ index += size;
+ }
+ maxStringLength = max;
+ // the class header information starts just after the constant pool
+ header = index;
+ }
+
+ /**
+ * Returns the class's access flags (see {@link Opcodes}). This value may
+ * not reflect Deprecated and Synthetic flags when bytecode is before 1.5
+ * and those flags are represented by attributes.
+ *
+ * @return the class access flags
+ *
+ * @see ClassVisitor#visit(int, int, String, String, String, String[])
+ */
+ public int getAccess() {
+ return readUnsignedShort(header);
+ }
+
+ /**
+ * Returns the internal name of the class (see
+ * {@link Type#getInternalName() getInternalName}).
+ *
+ * @return the internal class name
+ *
+ * @see ClassVisitor#visit(int, int, String, String, String, String[])
+ */
+ public String getClassName() {
+ return readClass(header + 2, new char[maxStringLength]);
+ }
+
+ /**
+ * Returns the internal of name of the super class (see
+ * {@link Type#getInternalName() getInternalName}). For interfaces, the
+ * super class is {@link Object}.
+ *
+ * @return the internal name of super class, or <tt>null</tt> for
+ * {@link Object} class.
+ *
+ * @see ClassVisitor#visit(int, int, String, String, String, String[])
+ */
+ public String getSuperName() {
+ int n = items[readUnsignedShort(header + 4)];
+ return n == 0 ? null : readUTF8(n, new char[maxStringLength]);
+ }
+
+ /**
+ * Returns the internal names of the class's interfaces (see
+ * {@link Type#getInternalName() getInternalName}).
+ *
+ * @return the array of internal names for all implemented interfaces or
+ * <tt>null</tt>.
+ *
+ * @see ClassVisitor#visit(int, int, String, String, String, String[])
+ */
+ public String[] getInterfaces() {
+ int index = header + 6;
+ int n = readUnsignedShort(index);
+ String[] interfaces = new String[n];
+ if (n > 0) {
+ char[] buf = new char[maxStringLength];
+ for (int i = 0; i < n; ++i) {
+ index += 2;
+ interfaces[i] = readClass(index, buf);
+ }
+ }
+ return interfaces;
+ }
+
+ /**
+ * Copies the constant pool data into the given {@link ClassWriter}. Should
+ * be called before the {@link #accept(ClassVisitor,int)} method.
+ *
+ * @param classWriter the {@link ClassWriter} to copy constant pool into.
+ */
+ void copyPool(final ClassWriter classWriter) {
+ char[] buf = new char[maxStringLength];
+ int ll = items.length;
+ Item[] items2 = new Item[ll];
+ for (int i = 1; i < ll; i++) {
+ int index = items[i];
+ int tag = b[index - 1];
+ Item item = new Item(i);
+ int nameType;
+ switch (tag) {
+ case ClassWriter.FIELD:
+ case ClassWriter.METH:
+ case ClassWriter.IMETH:
+ nameType = items[readUnsignedShort(index + 2)];
+ item.set(tag,
+ readClass(index, buf),
+ readUTF8(nameType, buf),
+ readUTF8(nameType + 2, buf));
+ break;
+
+ case ClassWriter.INT:
+ item.set(readInt(index));
+ break;
+
+ case ClassWriter.FLOAT:
+ item.set(Float.intBitsToFloat(readInt(index)));
+ break;
+
+ case ClassWriter.NAME_TYPE:
+ item.set(tag,
+ readUTF8(index, buf),
+ readUTF8(index + 2, buf),
+ null);
+ break;
+
+ case ClassWriter.LONG:
+ item.set(readLong(index));
+ ++i;
+ break;
+
+ case ClassWriter.DOUBLE:
+ item.set(Double.longBitsToDouble(readLong(index)));
+ ++i;
+ break;
+
+ case ClassWriter.UTF8: {
+ String s = strings[i];
+ if (s == null) {
+ index = items[i];
+ s = strings[i] = readUTF(index + 2,
+ readUnsignedShort(index),
+ buf);
+ }
+ item.set(tag, s, null, null);
+ }
+ break;
+
+ // case ClassWriter.STR:
+ // case ClassWriter.CLASS:
+ default:
+ item.set(tag, readUTF8(index, buf), null, null);
+ break;
+ }
+
+ int index2 = item.hashCode % items2.length;
+ item.next = items2[index2];
+ items2[index2] = item;
+ }
+
+ int off = items[1] - 1;
+ classWriter.pool.putByteArray(b, off, header - off);
+ classWriter.items = items2;
+ classWriter.threshold = (int) (0.75d * ll);
+ classWriter.index = ll;
+ }
+
+ /**
+ * Constructs a new {@link ClassReader} object.
+ *
+ * @param is an input stream from which to read the class.
+ * @throws IOException if a problem occurs during reading.
+ */
+ public ClassReader(final InputStream is) throws IOException {
+ this(readClass(is));
+ }
+
+ /**
+ * Constructs a new {@link ClassReader} object.
+ *
+ * @param name the fully qualified name of the class to be read.
+ * @throws IOException if an exception occurs during reading.
+ */
+ public ClassReader(final String name) throws IOException {
+ this(ClassLoader.getSystemResourceAsStream(name.replace('.', '/')
+ + ".class"));
+ }
+
+ /**
+ * Reads the bytecode of a class.
+ *
+ * @param is an input stream from which to read the class.
+ * @return the bytecode read from the given input stream.
+ * @throws IOException if a problem occurs during reading.
+ */
+ private static byte[] readClass(final InputStream is) throws IOException {
+ if (is == null) {
+ throw new IOException("Class not found");
+ }
+ byte[] b = new byte[is.available()];
+ int len = 0;
+ while (true) {
+ int n = is.read(b, len, b.length - len);
+ if (n == -1) {
+ if (len < b.length) {
+ byte[] c = new byte[len];
+ System.arraycopy(b, 0, c, 0, len);
+ b = c;
+ }
+ return b;
+ }
+ len += n;
+ if (len == b.length) {
+ byte[] c = new byte[b.length + 1000];
+ System.arraycopy(b, 0, c, 0, len);
+ b = c;
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Public methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Makes the given visitor visit the Java class of this {@link ClassReader}.
+ * This class is the one specified in the constructor (see
+ * {@link #ClassReader(byte[]) ClassReader}).
+ *
+ * @param classVisitor the visitor that must visit this class.
+ * @param flags option flags that can be used to modify the default behavior
+ * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES},
+ * {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
+ */
+ public void accept(final ClassVisitor classVisitor, final int flags) {
+ accept(classVisitor, new Attribute[0], flags);
+ }
+
+ /**
+ * Makes the given visitor visit the Java class of this {@link ClassReader}.
+ * This class is the one specified in the constructor (see
+ * {@link #ClassReader(byte[]) ClassReader}).
+ *
+ * @param classVisitor the visitor that must visit this class.
+ * @param attrs prototypes of the attributes that must be parsed during the
+ * visit of the class. Any attribute whose type is not equal to the
+ * type of one the prototypes will not be parsed: its byte array
+ * value will be passed unchanged to the ClassWriter. <i>This may
+ * corrupt it if this value contains references to the constant pool,
+ * or has syntactic or semantic links with a class element that has
+ * been transformed by a class adapter between the reader and the
+ * writer</i>.
+ * @param flags option flags that can be used to modify the default behavior
+ * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES},
+ * {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
+ */
+ public void accept(
+ final ClassVisitor classVisitor,
+ final Attribute[] attrs,
+ final int flags)
+ {
+ byte[] b = this.b; // the bytecode array
+ char[] c = new char[maxStringLength]; // buffer used to read strings
+ int i, j, k; // loop variables
+ int u, v, w; // indexes in b
+ Attribute attr;
+
+ int access;
+ String name;
+ String desc;
+ String attrName;
+ String signature;
+ int anns = 0;
+ int ianns = 0;
+ Attribute cattrs = null;
+
+ // visits the header
+ u = header;
+ access = readUnsignedShort(u);
+ name = readClass(u + 2, c);
+ v = items[readUnsignedShort(u + 4)];
+ String superClassName = v == 0 ? null : readUTF8(v, c);
+ String[] implementedItfs = new String[readUnsignedShort(u + 6)];
+ w = 0;
+ u += 8;
+ for (i = 0; i < implementedItfs.length; ++i) {
+ implementedItfs[i] = readClass(u, c);
+ u += 2;
+ }
+
+ boolean skipCode = (flags & SKIP_CODE) != 0;
+ boolean skipDebug = (flags & SKIP_DEBUG) != 0;
+ boolean unzip = (flags & EXPAND_FRAMES) != 0;
+
+ // skips fields and methods
+ v = u;
+ i = readUnsignedShort(v);
+ v += 2;
+ for (; i > 0; --i) {
+ j = readUnsignedShort(v + 6);
+ v += 8;
+ for (; j > 0; --j) {
+ v += 6 + readInt(v + 2);
+ }
+ }
+ i = readUnsignedShort(v);
+ v += 2;
+ for (; i > 0; --i) {
+ j = readUnsignedShort(v + 6);
+ v += 8;
+ for (; j > 0; --j) {
+ v += 6 + readInt(v + 2);
+ }
+ }
+ // reads the class's attributes
+ signature = null;
+ String sourceFile = null;
+ String sourceDebug = null;
+ String enclosingOwner = null;
+ String enclosingName = null;
+ String enclosingDesc = null;
+
+ i = readUnsignedShort(v);
+ v += 2;
+ for (; i > 0; --i) {
+ attrName = readUTF8(v, c);
+ // tests are sorted in decreasing frequency order
+ // (based on frequencies observed on typical classes)
+ if ("SourceFile".equals(attrName)) {
+ sourceFile = readUTF8(v + 6, c);
+ } else if ("InnerClasses".equals(attrName)) {
+ w = v + 6;
+ } else if ("EnclosingMethod".equals(attrName)) {
+ enclosingOwner = readClass(v + 6, c);
+ int item = readUnsignedShort(v + 8);
+ if (item != 0) {
+ enclosingName = readUTF8(items[item], c);
+ enclosingDesc = readUTF8(items[item] + 2, c);
+ }
+ } else if (SIGNATURES && "Signature".equals(attrName)) {
+ signature = readUTF8(v + 6, c);
+ } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) {
+ anns = v + 6;
+ } else if ("Deprecated".equals(attrName)) {
+ access |= Opcodes.ACC_DEPRECATED;
+ } else if ("Synthetic".equals(attrName)) {
+ access |= Opcodes.ACC_SYNTHETIC;
+ } else if ("SourceDebugExtension".equals(attrName)) {
+ int len = readInt(v + 2);
+ sourceDebug = readUTF(v + 6, len, new char[len]);
+ } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) {
+ ianns = v + 6;
+ } else {
+ attr = readAttribute(attrs,
+ attrName,
+ v + 6,
+ readInt(v + 2),
+ c,
+ -1,
+ null);
+ if (attr != null) {
+ attr.next = cattrs;
+ cattrs = attr;
+ }
+ }
+ v += 6 + readInt(v + 2);
+ }
+ // calls the visit method
+ classVisitor.visit(readInt(4),
+ access,
+ name,
+ signature,
+ superClassName,
+ implementedItfs);
+
+ // calls the visitSource method
+ if (!skipDebug && (sourceFile != null || sourceDebug != null)) {
+ classVisitor.visitSource(sourceFile, sourceDebug);
+ }
+
+ // calls the visitOuterClass method
+ if (enclosingOwner != null) {
+ classVisitor.visitOuterClass(enclosingOwner,
+ enclosingName,
+ enclosingDesc);
+ }
+
+ // visits the class annotations
+ if (ANNOTATIONS) {
+ for (i = 1; i >= 0; --i) {
+ v = i == 0 ? ianns : anns;
+ if (v != 0) {
+ j = readUnsignedShort(v);
+ v += 2;
+ for (; j > 0; --j) {
+ v = readAnnotationValues(v + 2,
+ c,
+ true,
+ classVisitor.visitAnnotation(readUTF8(v, c), i != 0));
+ }
+ }
+ }
+ }
+
+ // visits the class attributes
+ while (cattrs != null) {
+ attr = cattrs.next;
+ cattrs.next = null;
+ classVisitor.visitAttribute(cattrs);
+ cattrs = attr;
+ }
+
+ // calls the visitInnerClass method
+ if (w != 0) {
+ i = readUnsignedShort(w);
+ w += 2;
+ for (; i > 0; --i) {
+ classVisitor.visitInnerClass(readUnsignedShort(w) == 0
+ ? null
+ : readClass(w, c), readUnsignedShort(w + 2) == 0
+ ? null
+ : readClass(w + 2, c), readUnsignedShort(w + 4) == 0
+ ? null
+ : readUTF8(w + 4, c), readUnsignedShort(w + 6));
+ w += 8;
+ }
+ }
+
+ // visits the fields
+ i = readUnsignedShort(u);
+ u += 2;
+ for (; i > 0; --i) {
+ access = readUnsignedShort(u);
+ name = readUTF8(u + 2, c);
+ desc = readUTF8(u + 4, c);
+ // visits the field's attributes and looks for a ConstantValue
+ // attribute
+ int fieldValueItem = 0;
+ signature = null;
+ anns = 0;
+ ianns = 0;
+ cattrs = null;
+
+ j = readUnsignedShort(u + 6);
+ u += 8;
+ for (; j > 0; --j) {
+ attrName = readUTF8(u, c);
+ // tests are sorted in decreasing frequency order
+ // (based on frequencies observed on typical classes)
+ if ("ConstantValue".equals(attrName)) {
+ fieldValueItem = readUnsignedShort(u + 6);
+ } else if (SIGNATURES && "Signature".equals(attrName)) {
+ signature = readUTF8(u + 6, c);
+ } else if ("Deprecated".equals(attrName)) {
+ access |= Opcodes.ACC_DEPRECATED;
+ } else if ("Synthetic".equals(attrName)) {
+ access |= Opcodes.ACC_SYNTHETIC;
+ } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) {
+ anns = u + 6;
+ } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) {
+ ianns = u + 6;
+ } else {
+ attr = readAttribute(attrs,
+ attrName,
+ u + 6,
+ readInt(u + 2),
+ c,
+ -1,
+ null);
+ if (attr != null) {
+ attr.next = cattrs;
+ cattrs = attr;
+ }
+ }
+ u += 6 + readInt(u + 2);
+ }
+ // visits the field
+ FieldVisitor fv = classVisitor.visitField(access,
+ name,
+ desc,
+ signature,
+ fieldValueItem == 0 ? null : readConst(fieldValueItem, c));
+ // visits the field annotations and attributes
+ if (fv != null) {
+ if (ANNOTATIONS) {
+ for (j = 1; j >= 0; --j) {
+ v = j == 0 ? ianns : anns;
+ if (v != 0) {
+ k = readUnsignedShort(v);
+ v += 2;
+ for (; k > 0; --k) {
+ v = readAnnotationValues(v + 2,
+ c,
+ true,
+ fv.visitAnnotation(readUTF8(v, c), j != 0));
+ }
+ }
+ }
+ }
+ while (cattrs != null) {
+ attr = cattrs.next;
+ cattrs.next = null;
+ fv.visitAttribute(cattrs);
+ cattrs = attr;
+ }
+ fv.visitEnd();
+ }
+ }
+
+ // visits the methods
+ i = readUnsignedShort(u);
+ u += 2;
+ for (; i > 0; --i) {
+ int u0 = u + 6;
+ access = readUnsignedShort(u);
+ name = readUTF8(u + 2, c);
+ desc = readUTF8(u + 4, c);
+ signature = null;
+ anns = 0;
+ ianns = 0;
+ int dann = 0;
+ int mpanns = 0;
+ int impanns = 0;
+ cattrs = null;
+ v = 0;
+ w = 0;
+
+ // looks for Code and Exceptions attributes
+ j = readUnsignedShort(u + 6);
+ u += 8;
+ for (; j > 0; --j) {
+ attrName = readUTF8(u, c);
+ int attrSize = readInt(u + 2);
+ u += 6;
+ // tests are sorted in decreasing frequency order
+ // (based on frequencies observed on typical classes)
+ if ("Code".equals(attrName)) {
+ if (!skipCode) {
+ v = u;
+ }
+ } else if ("Exceptions".equals(attrName)) {
+ w = u;
+ } else if (SIGNATURES && "Signature".equals(attrName)) {
+ signature = readUTF8(u, c);
+ } else if ("Deprecated".equals(attrName)) {
+ access |= Opcodes.ACC_DEPRECATED;
+ } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) {
+ anns = u;
+ } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) {
+ dann = u;
+ } else if ("Synthetic".equals(attrName)) {
+ access |= Opcodes.ACC_SYNTHETIC;
+ } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) {
+ ianns = u;
+ } else if (ANNOTATIONS && "RuntimeVisibleParameterAnnotations".equals(attrName))
+ {
+ mpanns = u;
+ } else if (ANNOTATIONS && "RuntimeInvisibleParameterAnnotations".equals(attrName))
+ {
+ impanns = u;
+ } else {
+ attr = readAttribute(attrs,
+ attrName,
+ u,
+ attrSize,
+ c,
+ -1,
+ null);
+ if (attr != null) {
+ attr.next = cattrs;
+ cattrs = attr;
+ }
+ }
+ u += attrSize;
+ }
+ // reads declared exceptions
+ String[] exceptions;
+ if (w == 0) {
+ exceptions = null;
+ } else {
+ exceptions = new String[readUnsignedShort(w)];
+ w += 2;
+ for (j = 0; j < exceptions.length; ++j) {
+ exceptions[j] = readClass(w, c);
+ w += 2;
+ }
+ }
+
+ // visits the method's code, if any
+ MethodVisitor mv = classVisitor.visitMethod(access,
+ name,
+ desc,
+ signature,
+ exceptions);
+
+ if (mv != null) {
+ /*
+ * if the returned MethodVisitor is in fact a MethodWriter, it
+ * means there is no method adapter between the reader and the
+ * writer. If, in addition, the writer's constant pool was
+ * copied from this reader (mw.cw.cr == this), and the signature
+ * and exceptions of the method have not been changed, then it
+ * is possible to skip all visit events and just copy the
+ * original code of the method to the writer (the access, name
+ * and descriptor can have been changed, this is not important
+ * since they are not copied as is from the reader).
+ */
+ if (WRITER && mv instanceof MethodWriter) {
+ MethodWriter mw = (MethodWriter) mv;
+ if (mw.cw.cr == this) {
+ if (signature == mw.signature) {
+ boolean sameExceptions = false;
+ if (exceptions == null) {
+ sameExceptions = mw.exceptionCount == 0;
+ } else {
+ if (exceptions.length == mw.exceptionCount) {
+ sameExceptions = true;
+ for (j = exceptions.length - 1; j >= 0; --j)
+ {
+ w -= 2;
+ if (mw.exceptions[j] != readUnsignedShort(w))
+ {
+ sameExceptions = false;
+ break;
+ }
+ }
+ }
+ }
+ if (sameExceptions) {
+ /*
+ * we do not copy directly the code into
+ * MethodWriter to save a byte array copy
+ * operation. The real copy will be done in
+ * ClassWriter.toByteArray().
+ */
+ mw.classReaderOffset = u0;
+ mw.classReaderLength = u - u0;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (ANNOTATIONS && dann != 0) {
+ AnnotationVisitor dv = mv.visitAnnotationDefault();
+ readAnnotationValue(dann, c, null, dv);
+ if (dv != null) {
+ dv.visitEnd();
+ }
+ }
+ if (ANNOTATIONS) {
+ for (j = 1; j >= 0; --j) {
+ w = j == 0 ? ianns : anns;
+ if (w != 0) {
+ k = readUnsignedShort(w);
+ w += 2;
+ for (; k > 0; --k) {
+ w = readAnnotationValues(w + 2,
+ c,
+ true,
+ mv.visitAnnotation(readUTF8(w, c), j != 0));
+ }
+ }
+ }
+ }
+ if (ANNOTATIONS && mpanns != 0) {
+ readParameterAnnotations(mpanns, desc, c, true, mv);
+ }
+ if (ANNOTATIONS && impanns != 0) {
+ readParameterAnnotations(impanns, desc, c, false, mv);
+ }
+ while (cattrs != null) {
+ attr = cattrs.next;
+ cattrs.next = null;
+ mv.visitAttribute(cattrs);
+ cattrs = attr;
+ }
+ }
+
+ if (mv != null && v != 0) {
+ int maxStack = readUnsignedShort(v);
+ int maxLocals = readUnsignedShort(v + 2);
+ int codeLength = readInt(v + 4);
+ v += 8;
+
+ int codeStart = v;
+ int codeEnd = v + codeLength;
+
+ mv.visitCode();
+
+ // 1st phase: finds the labels
+ int label;
+ Label[] labels = new Label[codeLength + 2];
+ readLabel(codeLength + 1, labels);
+ while (v < codeEnd) {
+ w = v - codeStart;
+ int opcode = b[v] & 0xFF;
+ switch (ClassWriter.TYPE[opcode]) {
+ case ClassWriter.NOARG_INSN:
+ case ClassWriter.IMPLVAR_INSN:
+ v += 1;
+ break;
+ case ClassWriter.LABEL_INSN:
+ readLabel(w + readShort(v + 1), labels);
+ v += 3;
+ break;
+ case ClassWriter.LABELW_INSN:
+ readLabel(w + readInt(v + 1), labels);
+ v += 5;
+ break;
+ case ClassWriter.WIDE_INSN:
+ opcode = b[v + 1] & 0xFF;
+ if (opcode == Opcodes.IINC) {
+ v += 6;
+ } else {
+ v += 4;
+ }
+ break;
+ case ClassWriter.TABL_INSN:
+ // skips 0 to 3 padding bytes*
+ v = v + 4 - (w & 3);
+ // reads instruction
+ readLabel(w + readInt(v), labels);
+ j = readInt(v + 8) - readInt(v + 4) + 1;
+ v += 12;
+ for (; j > 0; --j) {
+ readLabel(w + readInt(v), labels);
+ v += 4;
+ }
+ break;
+ case ClassWriter.LOOK_INSN:
+ // skips 0 to 3 padding bytes*
+ v = v + 4 - (w & 3);
+ // reads instruction
+ readLabel(w + readInt(v), labels);
+ j = readInt(v + 4);
+ v += 8;
+ for (; j > 0; --j) {
+ readLabel(w + readInt(v + 4), labels);
+ v += 8;
+ }
+ break;
+ case ClassWriter.VAR_INSN:
+ case ClassWriter.SBYTE_INSN:
+ case ClassWriter.LDC_INSN:
+ v += 2;
+ break;
+ case ClassWriter.SHORT_INSN:
+ case ClassWriter.LDCW_INSN:
+ case ClassWriter.FIELDORMETH_INSN:
+ case ClassWriter.TYPE_INSN:
+ case ClassWriter.IINC_INSN:
+ v += 3;
+ break;
+ case ClassWriter.ITFMETH_INSN:
+ v += 5;
+ break;
+ // case MANA_INSN:
+ default:
+ v += 4;
+ break;
+ }
+ }
+ // parses the try catch entries
+ j = readUnsignedShort(v);
+ v += 2;
+ for (; j > 0; --j) {
+ Label start = readLabel(readUnsignedShort(v), labels);
+ Label end = readLabel(readUnsignedShort(v + 2), labels);
+ Label handler = readLabel(readUnsignedShort(v + 4), labels);
+ int type = readUnsignedShort(v + 6);
+ if (type == 0) {
+ mv.visitTryCatchBlock(start, end, handler, null);
+ } else {
+ mv.visitTryCatchBlock(start,
+ end,
+ handler,
+ readUTF8(items[type], c));
+ }
+ v += 8;
+ }
+ // parses the local variable, line number tables, and code
+ // attributes
+ int varTable = 0;
+ int varTypeTable = 0;
+ int stackMap = 0;
+ int frameCount = 0;
+ int frameMode = 0;
+ int frameOffset = 0;
+ int frameLocalCount = 0;
+ int frameLocalDiff = 0;
+ int frameStackCount = 0;
+ Object[] frameLocal = null;
+ Object[] frameStack = null;
+ boolean zip = true;
+ cattrs = null;
+ j = readUnsignedShort(v);
+ v += 2;
+ for (; j > 0; --j) {
+ attrName = readUTF8(v, c);
+ if ("LocalVariableTable".equals(attrName)) {
+ if (!skipDebug) {
+ varTable = v + 6;
+ k = readUnsignedShort(v + 6);
+ w = v + 8;
+ for (; k > 0; --k) {
+ label = readUnsignedShort(w);
+ if (labels[label] == null) {
+ readLabel(label, labels).status |= Label.DEBUG;
+ }
+ label += readUnsignedShort(w + 2);
+ if (labels[label] == null) {
+ readLabel(label, labels).status |= Label.DEBUG;
+ }
+ w += 10;
+ }
+ }
+ } else if ("LocalVariableTypeTable".equals(attrName)) {
+ varTypeTable = v + 6;
+ } else if ("LineNumberTable".equals(attrName)) {
+ if (!skipDebug) {
+ k = readUnsignedShort(v + 6);
+ w = v + 8;
+ for (; k > 0; --k) {
+ label = readUnsignedShort(w);
+ if (labels[label] == null) {
+ readLabel(label, labels).status |= Label.DEBUG;
+ }
+ labels[label].line = readUnsignedShort(w + 2);
+ w += 4;
+ }
+ }
+ } else if (FRAMES && "StackMapTable".equals(attrName)) {
+ if ((flags & SKIP_FRAMES) == 0) {
+ stackMap = v + 8;
+ frameCount = readUnsignedShort(v + 6);
+ }
+ /*
+ * here we do not extract the labels corresponding to
+ * the attribute content. This would require a full
+ * parsing of the attribute, which would need to be
+ * repeated in the second phase (see below). Instead the
+ * content of the attribute is read one frame at a time
+ * (i.e. after a frame has been visited, the next frame
+ * is read), and the labels it contains are also
+ * extracted one frame at a time. Thanks to the ordering
+ * of frames, having only a "one frame lookahead" is not
+ * a problem, i.e. it is not possible to see an offset
+ * smaller than the offset of the current insn and for
+ * which no Label exist.
+ */
+ // TODO true for frame offsets,
+ // but for UNINITIALIZED type offsets?
+ } else if (FRAMES && "StackMap".equals(attrName)) {
+ if ((flags & SKIP_FRAMES) == 0) {
+ stackMap = v + 8;
+ frameCount = readUnsignedShort(v + 6);
+ zip = false;
+ }
+ /*
+ * IMPORTANT! here we assume that the frames are
+ * ordered, as in the StackMapTable attribute, although
+ * this is not guaranteed by the attribute format.
+ */
+ } else {
+ for (k = 0; k < attrs.length; ++k) {
+ if (attrs[k].type.equals(attrName)) {
+ attr = attrs[k].read(this,
+ v + 6,
+ readInt(v + 2),
+ c,
+ codeStart - 8,
+ labels);
+ if (attr != null) {
+ attr.next = cattrs;
+ cattrs = attr;
+ }
+ }
+ }
+ }
+ v += 6 + readInt(v + 2);
+ }
+
+ // 2nd phase: visits each instruction
+ if (FRAMES && stackMap != 0) {
+ // creates the very first (implicit) frame from the method
+ // descriptor
+ frameLocal = new Object[maxLocals];
+ frameStack = new Object[maxStack];
+ if (unzip) {
+ int local = 0;
+ if ((access & Opcodes.ACC_STATIC) == 0) {
+ if ("<init>".equals(name)) {
+ frameLocal[local++] = Opcodes.UNINITIALIZED_THIS;
+ } else {
+ frameLocal[local++] = readClass(header + 2, c);
+ }
+ }
+ j = 1;
+ loop: while (true) {
+ k = j;
+ switch (desc.charAt(j++)) {
+ case 'Z':
+ case 'C':
+ case 'B':
+ case 'S':
+ case 'I':
+ frameLocal[local++] = Opcodes.INTEGER;
+ break;
+ case 'F':
+ frameLocal[local++] = Opcodes.FLOAT;
+ break;
+ case 'J':
+ frameLocal[local++] = Opcodes.LONG;
+ break;
+ case 'D':
+ frameLocal[local++] = Opcodes.DOUBLE;
+ break;
+ case '[':
+ while (desc.charAt(j) == '[') {
+ ++j;
+ }
+ if (desc.charAt(j) == 'L') {
+ ++j;
+ while (desc.charAt(j) != ';') {
+ ++j;
+ }
+ }
+ frameLocal[local++] = desc.substring(k, ++j);
+ break;
+ case 'L':
+ while (desc.charAt(j) != ';') {
+ ++j;
+ }
+ frameLocal[local++] = desc.substring(k + 1,
+ j++);
+ break;
+ default:
+ break loop;
+ }
+ }
+ frameLocalCount = local;
+ }
+ /*
+ * for the first explicit frame the offset is not
+ * offset_delta + 1 but only offset_delta; setting the
+ * implicit frame offset to -1 allow the use of the
+ * "offset_delta + 1" rule in all cases
+ */
+ frameOffset = -1;
+ }
+ v = codeStart;
+ Label l;
+ while (v < codeEnd) {
+ w = v - codeStart;
+
+ l = labels[w];
+ if (l != null) {
+ mv.visitLabel(l);
+ if (!skipDebug && l.line > 0) {
+ mv.visitLineNumber(l.line, l);
+ }
+ }
+
+ while (FRAMES && frameLocal != null
+ && (frameOffset == w || frameOffset == -1))
+ {
+ // if there is a frame for this offset,
+ // makes the visitor visit it,
+ // and reads the next frame if there is one.
+ if (!zip || unzip) {
+ mv.visitFrame(Opcodes.F_NEW,
+ frameLocalCount,
+ frameLocal,
+ frameStackCount,
+ frameStack);
+ } else if (frameOffset != -1) {
+ mv.visitFrame(frameMode,
+ frameLocalDiff,
+ frameLocal,
+ frameStackCount,
+ frameStack);
+ }
+
+ if (frameCount > 0) {
+ int tag, delta, n;
+ if (zip) {
+ tag = b[stackMap++] & 0xFF;
+ } else {
+ tag = MethodWriter.FULL_FRAME;
+ frameOffset = -1;
+ }
+ frameLocalDiff = 0;
+ if (tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME)
+ {
+ delta = tag;
+ frameMode = Opcodes.F_SAME;
+ frameStackCount = 0;
+ } else if (tag < MethodWriter.RESERVED) {
+ delta = tag
+ - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME;
+ stackMap = readFrameType(frameStack,
+ 0,
+ stackMap,
+ c,
+ labels);
+ frameMode = Opcodes.F_SAME1;
+ frameStackCount = 1;
+ } else {
+ delta = readUnsignedShort(stackMap);
+ stackMap += 2;
+ if (tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
+ {
+ stackMap = readFrameType(frameStack,
+ 0,
+ stackMap,
+ c,
+ labels);
+ frameMode = Opcodes.F_SAME1;
+ frameStackCount = 1;
+ } else if (tag >= MethodWriter.CHOP_FRAME
+ && tag < MethodWriter.SAME_FRAME_EXTENDED)
+ {
+ frameMode = Opcodes.F_CHOP;
+ frameLocalDiff = MethodWriter.SAME_FRAME_EXTENDED
+ - tag;
+ frameLocalCount -= frameLocalDiff;
+ frameStackCount = 0;
+ } else if (tag == MethodWriter.SAME_FRAME_EXTENDED)
+ {
+ frameMode = Opcodes.F_SAME;
+ frameStackCount = 0;
+ } else if (tag < MethodWriter.FULL_FRAME) {
+ j = unzip ? frameLocalCount : 0;
+ for (k = tag
+ - MethodWriter.SAME_FRAME_EXTENDED; k > 0; k--)
+ {
+ stackMap = readFrameType(frameLocal,
+ j++,
+ stackMap,
+ c,
+ labels);
+ }
+ frameMode = Opcodes.F_APPEND;
+ frameLocalDiff = tag
+ - MethodWriter.SAME_FRAME_EXTENDED;
+ frameLocalCount += frameLocalDiff;
+ frameStackCount = 0;
+ } else { // if (tag == FULL_FRAME) {
+ frameMode = Opcodes.F_FULL;
+ n = frameLocalDiff = frameLocalCount = readUnsignedShort(stackMap);
+ stackMap += 2;
+ for (j = 0; n > 0; n--) {
+ stackMap = readFrameType(frameLocal,
+ j++,
+ stackMap,
+ c,
+ labels);
+ }
+ n = frameStackCount = readUnsignedShort(stackMap);
+ stackMap += 2;
+ for (j = 0; n > 0; n--) {
+ stackMap = readFrameType(frameStack,
+ j++,
+ stackMap,
+ c,
+ labels);
+ }
+ }
+ }
+ frameOffset += delta + 1;
+ readLabel(frameOffset, labels);
+
+ --frameCount;
+ } else {
+ frameLocal = null;
+ }
+ }
+
+ int opcode = b[v] & 0xFF;
+ switch (ClassWriter.TYPE[opcode]) {
+ case ClassWriter.NOARG_INSN:
+ mv.visitInsn(opcode);
+ v += 1;
+ break;
+ case ClassWriter.IMPLVAR_INSN:
+ if (opcode > Opcodes.ISTORE) {
+ opcode -= 59; // ISTORE_0
+ mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2),
+ opcode & 0x3);
+ } else {
+ opcode -= 26; // ILOAD_0
+ mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2),
+ opcode & 0x3);
+ }
+ v += 1;
+ break;
+ case ClassWriter.LABEL_INSN:
+ mv.visitJumpInsn(opcode, labels[w
+ + readShort(v + 1)]);
+ v += 3;
+ break;
+ case ClassWriter.LABELW_INSN:
+ mv.visitJumpInsn(opcode - 33, labels[w
+ + readInt(v + 1)]);
+ v += 5;
+ break;
+ case ClassWriter.WIDE_INSN:
+ opcode = b[v + 1] & 0xFF;
+ if (opcode == Opcodes.IINC) {
+ mv.visitIincInsn(readUnsignedShort(v + 2),
+ readShort(v + 4));
+ v += 6;
+ } else {
+ mv.visitVarInsn(opcode,
+ readUnsignedShort(v + 2));
+ v += 4;
+ }
+ break;
+ case ClassWriter.TABL_INSN:
+ // skips 0 to 3 padding bytes
+ v = v + 4 - (w & 3);
+ // reads instruction
+ label = w + readInt(v);
+ int min = readInt(v + 4);
+ int max = readInt(v + 8);
+ v += 12;
+ Label[] table = new Label[max - min + 1];
+ for (j = 0; j < table.length; ++j) {
+ table[j] = labels[w + readInt(v)];
+ v += 4;
+ }
+ mv.visitTableSwitchInsn(min,
+ max,
+ labels[label],
+ table);
+ break;
+ case ClassWriter.LOOK_INSN:
+ // skips 0 to 3 padding bytes
+ v = v + 4 - (w & 3);
+ // reads instruction
+ label = w + readInt(v);
+ j = readInt(v + 4);
+ v += 8;
+ int[] keys = new int[j];
+ Label[] values = new Label[j];
+ for (j = 0; j < keys.length; ++j) {
+ keys[j] = readInt(v);
+ values[j] = labels[w + readInt(v + 4)];
+ v += 8;
+ }
+ mv.visitLookupSwitchInsn(labels[label],
+ keys,
+ values);
+ break;
+ case ClassWriter.VAR_INSN:
+ mv.visitVarInsn(opcode, b[v + 1] & 0xFF);
+ v += 2;
+ break;
+ case ClassWriter.SBYTE_INSN:
+ mv.visitIntInsn(opcode, b[v + 1]);
+ v += 2;
+ break;
+ case ClassWriter.SHORT_INSN:
+ mv.visitIntInsn(opcode, readShort(v + 1));
+ v += 3;
+ break;
+ case ClassWriter.LDC_INSN:
+ mv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c));
+ v += 2;
+ break;
+ case ClassWriter.LDCW_INSN:
+ mv.visitLdcInsn(readConst(readUnsignedShort(v + 1),
+ c));
+ v += 3;
+ break;
+ case ClassWriter.FIELDORMETH_INSN:
+ case ClassWriter.ITFMETH_INSN:
+ int cpIndex = items[readUnsignedShort(v + 1)];
+ String iowner = readClass(cpIndex, c);
+ cpIndex = items[readUnsignedShort(cpIndex + 2)];
+ String iname = readUTF8(cpIndex, c);
+ String idesc = readUTF8(cpIndex + 2, c);
+ if (opcode < Opcodes.INVOKEVIRTUAL) {
+ mv.visitFieldInsn(opcode, iowner, iname, idesc);
+ } else {
+ mv.visitMethodInsn(opcode, iowner, iname, idesc);
+ }
+ if (opcode == Opcodes.INVOKEINTERFACE) {
+ v += 5;
+ } else {
+ v += 3;
+ }
+ break;
+ case ClassWriter.TYPE_INSN:
+ mv.visitTypeInsn(opcode, readClass(v + 1, c));
+ v += 3;
+ break;
+ case ClassWriter.IINC_INSN:
+ mv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]);
+ v += 3;
+ break;
+ // case MANA_INSN:
+ default:
+ mv.visitMultiANewArrayInsn(readClass(v + 1, c),
+ b[v + 3] & 0xFF);
+ v += 4;
+ break;
+ }
+ }
+ l = labels[codeEnd - codeStart];
+ if (l != null) {
+ mv.visitLabel(l);
+ }
+ // visits the local variable tables
+ if (!skipDebug && varTable != 0) {
+ int[] typeTable = null;
+ if (varTypeTable != 0) {
+ k = readUnsignedShort(varTypeTable) * 3;
+ w = varTypeTable + 2;
+ typeTable = new int[k];
+ while (k > 0) {
+ typeTable[--k] = w + 6; // signature
+ typeTable[--k] = readUnsignedShort(w + 8); // index
+ typeTable[--k] = readUnsignedShort(w); // start
+ w += 10;
+ }
+ }
+ k = readUnsignedShort(varTable);
+ w = varTable + 2;
+ for (; k > 0; --k) {
+ int start = readUnsignedShort(w);
+ int length = readUnsignedShort(w + 2);
+ int index = readUnsignedShort(w + 8);
+ String vsignature = null;
+ if (typeTable != null) {
+ for (int a = 0; a < typeTable.length; a += 3) {
+ if (typeTable[a] == start
+ && typeTable[a + 1] == index)
+ {
+ vsignature = readUTF8(typeTable[a + 2], c);
+ break;
+ }
+ }
+ }
+ mv.visitLocalVariable(readUTF8(w + 4, c),
+ readUTF8(w + 6, c),
+ vsignature,
+ labels[start],
+ labels[start + length],
+ index);
+ w += 10;
+ }
+ }
+ // visits the other attributes
+ while (cattrs != null) {
+ attr = cattrs.next;
+ cattrs.next = null;
+ mv.visitAttribute(cattrs);
+ cattrs = attr;
+ }
+ // visits the max stack and max locals values
+ mv.visitMaxs(maxStack, maxLocals);
+ }
+
+ if (mv != null) {
+ mv.visitEnd();
+ }
+ }
+
+ // visits the end of the class
+ classVisitor.visitEnd();
+ }
+
+ /**
+ * Reads parameter annotations and makes the given visitor visit them.
+ *
+ * @param v start offset in {@link #b b} of the annotations to be read.
+ * @param desc the method descriptor.
+ * @param buf buffer to be used to call {@link #readUTF8 readUTF8},
+ * {@link #readClass(int,char[]) readClass} or
+ * {@link #readConst readConst}.
+ * @param visible <tt>true</tt> if the annotations to be read are visible
+ * at runtime.
+ * @param mv the visitor that must visit the annotations.
+ */
+ private void readParameterAnnotations(
+ int v,
+ final String desc,
+ final char[] buf,
+ final boolean visible,
+ final MethodVisitor mv)
+ {
+ int i;
+ int n = b[v++] & 0xFF;
+ // workaround for a bug in javac (javac compiler generates a parameter
+ // annotation array whose size is equal to the number of parameters in
+ // the Java source file, while it should generate an array whose size is
+ // equal to the number of parameters in the method descriptor - which
+ // includes the synthetic parameters added by the compiler). This work-
+ // around supposes that the synthetic parameters are the first ones.
+ int synthetics = Type.getArgumentTypes(desc).length - n;
+ AnnotationVisitor av;
+ for (i = 0; i < synthetics; ++i) {
+ // virtual annotation to detect synthetic parameters in MethodWriter
+ av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", false);
+ if (av != null) {
+ av.visitEnd();
+ }
+ }
+ for (; i < n + synthetics; ++i) {
+ int j = readUnsignedShort(v);
+ v += 2;
+ for (; j > 0; --j) {
+ av = mv.visitParameterAnnotation(i, readUTF8(v, buf), visible);
+ v = readAnnotationValues(v + 2, buf, true, av);
+ }
+ }
+ }
+
+ /**
+ * Reads the values of an annotation and makes the given visitor visit them.
+ *
+ * @param v the start offset in {@link #b b} of the values to be read
+ * (including the unsigned short that gives the number of values).
+ * @param buf buffer to be used to call {@link #readUTF8 readUTF8},
+ * {@link #readClass(int,char[]) readClass} or
+ * {@link #readConst readConst}.
+ * @param named if the annotation values are named or not.
+ * @param av the visitor that must visit the values.
+ * @return the end offset of the annotation values.
+ */
+ private int readAnnotationValues(
+ int v,
+ final char[] buf,
+ final boolean named,
+ final AnnotationVisitor av)
+ {
+ int i = readUnsignedShort(v);
+ v += 2;
+ if (named) {
+ for (; i > 0; --i) {
+ v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av);
+ }
+ } else {
+ for (; i > 0; --i) {
+ v = readAnnotationValue(v, buf, null, av);
+ }
+ }
+ if (av != null) {
+ av.visitEnd();
+ }
+ return v;
+ }
+
+ /**
+ * Reads a value of an annotation and makes the given visitor visit it.
+ *
+ * @param v the start offset in {@link #b b} of the value to be read (<i>not
+ * including the value name constant pool index</i>).
+ * @param buf buffer to be used to call {@link #readUTF8 readUTF8},
+ * {@link #readClass(int,char[]) readClass} or
+ * {@link #readConst readConst}.
+ * @param name the name of the value to be read.
+ * @param av the visitor that must visit the value.
+ * @return the end offset of the annotation value.
+ */
+ private int readAnnotationValue(
+ int v,
+ final char[] buf,
+ final String name,
+ final AnnotationVisitor av)
+ {
+ int i;
+ if (av == null) {
+ switch (b[v] & 0xFF) {
+ case 'e': // enum_const_value
+ return v + 5;
+ case '@': // annotation_value
+ return readAnnotationValues(v + 3, buf, true, null);
+ case '[': // array_value
+ return readAnnotationValues(v + 1, buf, false, null);
+ default:
+ return v + 3;
+ }
+ }
+ switch (b[v++] & 0xFF) {
+ case 'I': // pointer to CONSTANT_Integer
+ case 'J': // pointer to CONSTANT_Long
+ case 'F': // pointer to CONSTANT_Float
+ case 'D': // pointer to CONSTANT_Double
+ av.visit(name, readConst(readUnsignedShort(v), buf));
+ v += 2;
+ break;
+ case 'B': // pointer to CONSTANT_Byte
+ av.visit(name,
+ new Byte((byte) readInt(items[readUnsignedShort(v)])));
+ v += 2;
+ break;
+ case 'Z': // pointer to CONSTANT_Boolean
+ av.visit(name, readInt(items[readUnsignedShort(v)]) == 0
+ ? Boolean.FALSE
+ : Boolean.TRUE);
+ v += 2;
+ break;
+ case 'S': // pointer to CONSTANT_Short
+ av.visit(name,
+ new Short((short) readInt(items[readUnsignedShort(v)])));
+ v += 2;
+ break;
+ case 'C': // pointer to CONSTANT_Char
+ av.visit(name,
+ new Character((char) readInt(items[readUnsignedShort(v)])));
+ v += 2;
+ break;
+ case 's': // pointer to CONSTANT_Utf8
+ av.visit(name, readUTF8(v, buf));
+ v += 2;
+ break;
+ case 'e': // enum_const_value
+ av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf));
+ v += 4;
+ break;
+ case 'c': // class_info
+ av.visit(name, Type.getType(readUTF8(v, buf)));
+ v += 2;
+ break;
+ case '@': // annotation_value
+ v = readAnnotationValues(v + 2,
+ buf,
+ true,
+ av.visitAnnotation(name, readUTF8(v, buf)));
+ break;
+ case '[': // array_value
+ int size = readUnsignedShort(v);
+ v += 2;
+ if (size == 0) {
+ return readAnnotationValues(v - 2,
+ buf,
+ false,
+ av.visitArray(name));
+ }
+ switch (this.b[v++] & 0xFF) {
+ case 'B':
+ byte[] bv = new byte[size];
+ for (i = 0; i < size; i++) {
+ bv[i] = (byte) readInt(items[readUnsignedShort(v)]);
+ v += 3;
+ }
+ av.visit(name, bv);
+ --v;
+ break;
+ case 'Z':
+ boolean[] zv = new boolean[size];
+ for (i = 0; i < size; i++) {
+ zv[i] = readInt(items[readUnsignedShort(v)]) != 0;
+ v += 3;
+ }
+ av.visit(name, zv);
+ --v;
+ break;
+ case 'S':
+ short[] sv = new short[size];
+ for (i = 0; i < size; i++) {
+ sv[i] = (short) readInt(items[readUnsignedShort(v)]);
+ v += 3;
+ }
+ av.visit(name, sv);
+ --v;
+ break;
+ case 'C':
+ char[] cv = new char[size];
+ for (i = 0; i < size; i++) {
+ cv[i] = (char) readInt(items[readUnsignedShort(v)]);
+ v += 3;
+ }
+ av.visit(name, cv);
+ --v;
+ break;
+ case 'I':
+ int[] iv = new int[size];
+ for (i = 0; i < size; i++) {
+ iv[i] = readInt(items[readUnsignedShort(v)]);
+ v += 3;
+ }
+ av.visit(name, iv);
+ --v;
+ break;
+ case 'J':
+ long[] lv = new long[size];
+ for (i = 0; i < size; i++) {
+ lv[i] = readLong(items[readUnsignedShort(v)]);
+ v += 3;
+ }
+ av.visit(name, lv);
+ --v;
+ break;
+ case 'F':
+ float[] fv = new float[size];
+ for (i = 0; i < size; i++) {
+ fv[i] = Float.intBitsToFloat(readInt(items[readUnsignedShort(v)]));
+ v += 3;
+ }
+ av.visit(name, fv);
+ --v;
+ break;
+ case 'D':
+ double[] dv = new double[size];
+ for (i = 0; i < size; i++) {
+ dv[i] = Double.longBitsToDouble(readLong(items[readUnsignedShort(v)]));
+ v += 3;
+ }
+ av.visit(name, dv);
+ --v;
+ break;
+ default:
+ v = readAnnotationValues(v - 3,
+ buf,
+ false,
+ av.visitArray(name));
+ }
+ }
+ return v;
+ }
+
+ private int readFrameType(
+ final Object[] frame,
+ final int index,
+ int v,
+ final char[] buf,
+ final Label[] labels)
+ {
+ int type = b[v++] & 0xFF;
+ switch (type) {
+ case 0:
+ frame[index] = Opcodes.TOP;
+ break;
+ case 1:
+ frame[index] = Opcodes.INTEGER;
+ break;
+ case 2:
+ frame[index] = Opcodes.FLOAT;
+ break;
+ case 3:
+ frame[index] = Opcodes.DOUBLE;
+ break;
+ case 4:
+ frame[index] = Opcodes.LONG;
+ break;
+ case 5:
+ frame[index] = Opcodes.NULL;
+ break;
+ case 6:
+ frame[index] = Opcodes.UNINITIALIZED_THIS;
+ break;
+ case 7: // Object
+ frame[index] = readClass(v, buf);
+ v += 2;
+ break;
+ default: // Uninitialized
+ frame[index] = readLabel(readUnsignedShort(v), labels);
+ v += 2;
+ }
+ return v;
+ }
+
+ /**
+ * Returns the label corresponding to the given offset. The default
+ * implementation of this method creates a label for the given offset if it
+ * has not been already created.
+ *
+ * @param offset a bytecode offset in a method.
+ * @param labels the already created labels, indexed by their offset. If a
+ * label already exists for offset this method must not create a new
+ * one. Otherwise it must store the new label in this array.
+ * @return a non null Label, which must be equal to labels[offset].
+ */
+ protected Label readLabel(int offset, Label[] labels) {
+ if (labels[offset] == null) {
+ labels[offset] = new Label();
+ }
+ return labels[offset];
+ }
+
+ /**
+ * Reads an attribute in {@link #b b}.
+ *
+ * @param attrs prototypes of the attributes that must be parsed during the
+ * visit of the class. Any attribute whose type is not equal to the
+ * type of one the prototypes is ignored (i.e. an empty
+ * {@link Attribute} instance is returned).
+ * @param type the type of the attribute.
+ * @param off index of the first byte of the attribute's content in
+ * {@link #b b}. The 6 attribute header bytes, containing the type
+ * and the length of the attribute, are not taken into account here
+ * (they have already been read).
+ * @param len the length of the attribute's content.
+ * @param buf buffer to be used to call {@link #readUTF8 readUTF8},
+ * {@link #readClass(int,char[]) readClass} or
+ * {@link #readConst readConst}.
+ * @param codeOff index of the first byte of code's attribute content in
+ * {@link #b b}, or -1 if the attribute to be read is not a code
+ * attribute. The 6 attribute header bytes, containing the type and
+ * the length of the attribute, are not taken into account here.
+ * @param labels the labels of the method's code, or <tt>null</tt> if the
+ * attribute to be read is not a code attribute.
+ * @return the attribute that has been read, or <tt>null</tt> to skip this
+ * attribute.
+ */
+ private Attribute readAttribute(
+ final Attribute[] attrs,
+ final String type,
+ final int off,
+ final int len,
+ final char[] buf,
+ final int codeOff,
+ final Label[] labels)
+ {
+ for (int i = 0; i < attrs.length; ++i) {
+ if (attrs[i].type.equals(type)) {
+ return attrs[i].read(this, off, len, buf, codeOff, labels);
+ }
+ }
+ return new Attribute(type).read(this, off, len, null, -1, null);
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods: low level parsing
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the start index of the constant pool item in {@link #b b}, plus
+ * one. <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @param item the index a constant pool item.
+ * @return the start index of the constant pool item in {@link #b b}, plus
+ * one.
+ */
+ public int getItem(final int item) {
+ return items[item];
+ }
+
+ /**
+ * Reads a byte value in {@link #b b}. <i>This method is intended for
+ * {@link Attribute} sub classes, and is normally not needed by class
+ * generators or adapters.</i>
+ *
+ * @param index the start index of the value to be read in {@link #b b}.
+ * @return the read value.
+ */
+ public int readByte(final int index) {
+ return b[index] & 0xFF;
+ }
+
+ /**
+ * Reads an unsigned short value in {@link #b b}. <i>This method is
+ * intended for {@link Attribute} sub classes, and is normally not needed by
+ * class generators or adapters.</i>
+ *
+ * @param index the start index of the value to be read in {@link #b b}.
+ * @return the read value.
+ */
+ public int readUnsignedShort(final int index) {
+ byte[] b = this.b;
+ return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
+ }
+
+ /**
+ * Reads a signed short value in {@link #b b}. <i>This method is intended
+ * for {@link Attribute} sub classes, and is normally not needed by class
+ * generators or adapters.</i>
+ *
+ * @param index the start index of the value to be read in {@link #b b}.
+ * @return the read value.
+ */
+ public short readShort(final int index) {
+ byte[] b = this.b;
+ return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
+ }
+
+ /**
+ * Reads a signed int value in {@link #b b}. <i>This method is intended for
+ * {@link Attribute} sub classes, and is normally not needed by class
+ * generators or adapters.</i>
+ *
+ * @param index the start index of the value to be read in {@link #b b}.
+ * @return the read value.
+ */
+ public int readInt(final int index) {
+ byte[] b = this.b;
+ return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
+ | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
+ }
+
+ /**
+ * Reads a signed long value in {@link #b b}. <i>This method is intended
+ * for {@link Attribute} sub classes, and is normally not needed by class
+ * generators or adapters.</i>
+ *
+ * @param index the start index of the value to be read in {@link #b b}.
+ * @return the read value.
+ */
+ public long readLong(final int index) {
+ long l1 = readInt(index);
+ long l0 = readInt(index + 4) & 0xFFFFFFFFL;
+ return (l1 << 32) | l0;
+ }
+
+ /**
+ * Reads an UTF8 string constant pool item in {@link #b b}. <i>This method
+ * is intended for {@link Attribute} sub classes, and is normally not needed
+ * by class generators or adapters.</i>
+ *
+ * @param index the start index of an unsigned short value in {@link #b b},
+ * whose value is the index of an UTF8 constant pool item.
+ * @param buf buffer to be used to read the item. This buffer must be
+ * sufficiently large. It is not automatically resized.
+ * @return the String corresponding to the specified UTF8 item.
+ */
+ public String readUTF8(int index, final char[] buf) {
+ int item = readUnsignedShort(index);
+ String s = strings[item];
+ if (s != null) {
+ return s;
+ }
+ index = items[item];
+ return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf);
+ }
+
+ /**
+ * Reads UTF8 string in {@link #b b}.
+ *
+ * @param index start offset of the UTF8 string to be read.
+ * @param utfLen length of the UTF8 string to be read.
+ * @param buf buffer to be used to read the string. This buffer must be
+ * sufficiently large. It is not automatically resized.
+ * @return the String corresponding to the specified UTF8 string.
+ */
+ private String readUTF(int index, final int utfLen, final char[] buf) {
+ int endIndex = index + utfLen;
+ byte[] b = this.b;
+ int strLen = 0;
+ int c, d, e;
+ while (index < endIndex) {
+ c = b[index++] & 0xFF;
+ switch (c >> 4) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ // 0xxxxxxx
+ buf[strLen++] = (char) c;
+ break;
+ case 12:
+ case 13:
+ // 110x xxxx 10xx xxxx
+ d = b[index++];
+ buf[strLen++] = (char) (((c & 0x1F) << 6) | (d & 0x3F));
+ break;
+ default:
+ // 1110 xxxx 10xx xxxx 10xx xxxx
+ d = b[index++];
+ e = b[index++];
+ buf[strLen++] = (char) (((c & 0x0F) << 12)
+ | ((d & 0x3F) << 6) | (e & 0x3F));
+ break;
+ }
+ }
+ return new String(buf, 0, strLen);
+ }
+
+ /**
+ * Reads a class constant pool item in {@link #b b}. <i>This method is
+ * intended for {@link Attribute} sub classes, and is normally not needed by
+ * class generators or adapters.</i>
+ *
+ * @param index the start index of an unsigned short value in {@link #b b},
+ * whose value is the index of a class constant pool item.
+ * @param buf buffer to be used to read the item. This buffer must be
+ * sufficiently large. It is not automatically resized.
+ * @return the String corresponding to the specified class item.
+ */
+ public String readClass(final int index, final char[] buf) {
+ // computes the start index of the CONSTANT_Class item in b
+ // and reads the CONSTANT_Utf8 item designated by
+ // the first two bytes of this CONSTANT_Class item
+ return readUTF8(items[readUnsignedShort(index)], buf);
+ }
+
+ /**
+ * Reads a numeric or string constant pool item in {@link #b b}. <i>This
+ * method is intended for {@link Attribute} sub classes, and is normally not
+ * needed by class generators or adapters.</i>
+ *
+ * @param item the index of a constant pool item.
+ * @param buf buffer to be used to read the item. This buffer must be
+ * sufficiently large. It is not automatically resized.
+ * @return the {@link Integer}, {@link Float}, {@link Long},
+ * {@link Double}, {@link String} or {@link Type} corresponding to
+ * the given constant pool item.
+ */
+ public Object readConst(final int item, final char[] buf) {
+ int index = items[item];
+ switch (b[index - 1]) {
+ case ClassWriter.INT:
+ return new Integer(readInt(index));
+ case ClassWriter.FLOAT:
+ return new Float(Float.intBitsToFloat(readInt(index)));
+ case ClassWriter.LONG:
+ return new Long(readLong(index));
+ case ClassWriter.DOUBLE:
+ return new Double(Double.longBitsToDouble(readLong(index)));
+ case ClassWriter.CLASS:
+ return Type.getObjectType(readUTF8(index, buf));
+ // case ClassWriter.STR:
+ default:
+ return readUTF8(index, buf);
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/ClassVisitor.java b/cglib-and-asm/src/org/mockito/asm/ClassVisitor.java
new file mode 100644
index 0000000..22180bc
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/ClassVisitor.java
@@ -0,0 +1,196 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * A visitor to visit a Java class. The methods of this interface must be called
+ * in the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [
+ * <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
+ * <tt>visitAttribute</tt> )* (<tt>visitInnerClass</tt> |
+ * <tt>visitField</tt> | <tt>visitMethod</tt> )* <tt>visitEnd</tt>.
+ *
+ * @author Eric Bruneton
+ */
+public interface ClassVisitor {
+
+ /**
+ * Visits the header of the class.
+ *
+ * @param version the class version.
+ * @param access the class's access flags (see {@link Opcodes}). This
+ * parameter also indicates if the class is deprecated.
+ * @param name the internal name of the class (see
+ * {@link Type#getInternalName() getInternalName}).
+ * @param signature the signature of this class. May be <tt>null</tt> if
+ * the class is not a generic one, and does not extend or implement
+ * generic classes or interfaces.
+ * @param superName the internal of name of the super class (see
+ * {@link Type#getInternalName() getInternalName}). For interfaces,
+ * the super class is {@link Object}. May be <tt>null</tt>, but
+ * only for the {@link Object} class.
+ * @param interfaces the internal names of the class's interfaces (see
+ * {@link Type#getInternalName() getInternalName}). May be
+ * <tt>null</tt>.
+ */
+ void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces);
+
+ /**
+ * Visits the source of the class.
+ *
+ * @param source the name of the source file from which the class was
+ * compiled. May be <tt>null</tt>.
+ * @param debug additional debug information to compute the correspondance
+ * between source and compiled elements of the class. May be
+ * <tt>null</tt>.
+ */
+ void visitSource(String source, String debug);
+
+ /**
+ * Visits the enclosing class of the class. This method must be called only
+ * if the class has an enclosing class.
+ *
+ * @param owner internal name of the enclosing class of the class.
+ * @param name the name of the method that contains the class, or
+ * <tt>null</tt> if the class is not enclosed in a method of its
+ * enclosing class.
+ * @param desc the descriptor of the method that contains the class, or
+ * <tt>null</tt> if the class is not enclosed in a method of its
+ * enclosing class.
+ */
+ void visitOuterClass(String owner, String name, String desc);
+
+ /**
+ * Visits an annotation of the class.
+ *
+ * @param desc the class descriptor of the annotation class.
+ * @param visible <tt>true</tt> if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or <tt>null</tt> if
+ * this visitor is not interested in visiting this annotation.
+ */
+ AnnotationVisitor visitAnnotation(String desc, boolean visible);
+
+ /**
+ * Visits a non standard attribute of the class.
+ *
+ * @param attr an attribute.
+ */
+ void visitAttribute(Attribute attr);
+
+ /**
+ * Visits information about an inner class. This inner class is not
+ * necessarily a member of the class being visited.
+ *
+ * @param name the internal name of an inner class (see
+ * {@link Type#getInternalName() getInternalName}).
+ * @param outerName the internal name of the class to which the inner class
+ * belongs (see {@link Type#getInternalName() getInternalName}). May
+ * be <tt>null</tt> for not member classes.
+ * @param innerName the (simple) name of the inner class inside its
+ * enclosing class. May be <tt>null</tt> for anonymous inner
+ * classes.
+ * @param access the access flags of the inner class as originally declared
+ * in the enclosing class.
+ */
+ void visitInnerClass(
+ String name,
+ String outerName,
+ String innerName,
+ int access);
+
+ /**
+ * Visits a field of the class.
+ *
+ * @param access the field's access flags (see {@link Opcodes}). This
+ * parameter also indicates if the field is synthetic and/or
+ * deprecated.
+ * @param name the field's name.
+ * @param desc the field's descriptor (see {@link Type Type}).
+ * @param signature the field's signature. May be <tt>null</tt> if the
+ * field's type does not use generic types.
+ * @param value the field's initial value. This parameter, which may be
+ * <tt>null</tt> if the field does not have an initial value, must
+ * be an {@link Integer}, a {@link Float}, a {@link Long}, a
+ * {@link Double} or a {@link String} (for <tt>int</tt>,
+ * <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields
+ * respectively). <i>This parameter is only used for static fields</i>.
+ * Its value is ignored for non static fields, which must be
+ * initialized through bytecode instructions in constructors or
+ * methods.
+ * @return a visitor to visit field annotations and attributes, or
+ * <tt>null</tt> if this class visitor is not interested in
+ * visiting these annotations and attributes.
+ */
+ FieldVisitor visitField(
+ int access,
+ String name,
+ String desc,
+ String signature,
+ Object value);
+
+ /**
+ * Visits a method of the class. This method <i>must</i> return a new
+ * {@link MethodVisitor} instance (or <tt>null</tt>) each time it is
+ * called, i.e., it should not return a previously returned visitor.
+ *
+ * @param access the method's access flags (see {@link Opcodes}). This
+ * parameter also indicates if the method is synthetic and/or
+ * deprecated.
+ * @param name the method's name.
+ * @param desc the method's descriptor (see {@link Type Type}).
+ * @param signature the method's signature. May be <tt>null</tt> if the
+ * method parameters, return type and exceptions do not use generic
+ * types.
+ * @param exceptions the internal names of the method's exception classes
+ * (see {@link Type#getInternalName() getInternalName}). May be
+ * <tt>null</tt>.
+ * @return an object to visit the byte code of the method, or <tt>null</tt>
+ * if this class visitor is not interested in visiting the code of
+ * this method.
+ */
+ MethodVisitor visitMethod(
+ int access,
+ String name,
+ String desc,
+ String signature,
+ String[] exceptions);
+
+ /**
+ * Visits the end of the class. This method, which is the last one to be
+ * called, is used to inform the visitor that all the fields and methods of
+ * the class have been visited.
+ */
+ void visitEnd();
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/ClassWriter.java b/cglib-and-asm/src/org/mockito/asm/ClassWriter.java
new file mode 100644
index 0000000..366f568
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/ClassWriter.java
@@ -0,0 +1,1344 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * A {@link ClassVisitor} that generates classes in bytecode form. More
+ * precisely this visitor generates a byte array conforming to the Java class
+ * file format. It can be used alone, to generate a Java class "from scratch",
+ * or with one or more {@link ClassReader ClassReader} and adapter class visitor
+ * to generate a modified class from one or more existing Java classes.
+ *
+ * @author Eric Bruneton
+ */
+public class ClassWriter implements ClassVisitor {
+
+ /**
+ * Flag to automatically compute the maximum stack size and the maximum
+ * number of local variables of methods. If this flag is set, then the
+ * arguments of the {@link MethodVisitor#visitMaxs visitMaxs} method of the
+ * {@link MethodVisitor} returned by the {@link #visitMethod visitMethod}
+ * method will be ignored, and computed automatically from the signature and
+ * the bytecode of each method.
+ *
+ * @see #ClassWriter(int)
+ */
+ public static final int COMPUTE_MAXS = 1;
+
+ /**
+ * Flag to automatically compute the stack map frames of methods from
+ * scratch. If this flag is set, then the calls to the
+ * {@link MethodVisitor#visitFrame} method are ignored, and the stack map
+ * frames are recomputed from the methods bytecode. The arguments of the
+ * {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and
+ * recomputed from the bytecode. In other words, computeFrames implies
+ * computeMaxs.
+ *
+ * @see #ClassWriter(int)
+ */
+ public static final int COMPUTE_FRAMES = 2;
+
+ /**
+ * The type of instructions without any argument.
+ */
+ static final int NOARG_INSN = 0;
+
+ /**
+ * The type of instructions with an signed byte argument.
+ */
+ static final int SBYTE_INSN = 1;
+
+ /**
+ * The type of instructions with an signed short argument.
+ */
+ static final int SHORT_INSN = 2;
+
+ /**
+ * The type of instructions with a local variable index argument.
+ */
+ static final int VAR_INSN = 3;
+
+ /**
+ * The type of instructions with an implicit local variable index argument.
+ */
+ static final int IMPLVAR_INSN = 4;
+
+ /**
+ * The type of instructions with a type descriptor argument.
+ */
+ static final int TYPE_INSN = 5;
+
+ /**
+ * The type of field and method invocations instructions.
+ */
+ static final int FIELDORMETH_INSN = 6;
+
+ /**
+ * The type of the INVOKEINTERFACE instruction.
+ */
+ static final int ITFMETH_INSN = 7;
+
+ /**
+ * The type of instructions with a 2 bytes bytecode offset label.
+ */
+ static final int LABEL_INSN = 8;
+
+ /**
+ * The type of instructions with a 4 bytes bytecode offset label.
+ */
+ static final int LABELW_INSN = 9;
+
+ /**
+ * The type of the LDC instruction.
+ */
+ static final int LDC_INSN = 10;
+
+ /**
+ * The type of the LDC_W and LDC2_W instructions.
+ */
+ static final int LDCW_INSN = 11;
+
+ /**
+ * The type of the IINC instruction.
+ */
+ static final int IINC_INSN = 12;
+
+ /**
+ * The type of the TABLESWITCH instruction.
+ */
+ static final int TABL_INSN = 13;
+
+ /**
+ * The type of the LOOKUPSWITCH instruction.
+ */
+ static final int LOOK_INSN = 14;
+
+ /**
+ * The type of the MULTIANEWARRAY instruction.
+ */
+ static final int MANA_INSN = 15;
+
+ /**
+ * The type of the WIDE instruction.
+ */
+ static final int WIDE_INSN = 16;
+
+ /**
+ * The instruction types of all JVM opcodes.
+ */
+ static final byte[] TYPE;
+
+ /**
+ * The type of CONSTANT_Class constant pool items.
+ */
+ static final int CLASS = 7;
+
+ /**
+ * The type of CONSTANT_Fieldref constant pool items.
+ */
+ static final int FIELD = 9;
+
+ /**
+ * The type of CONSTANT_Methodref constant pool items.
+ */
+ static final int METH = 10;
+
+ /**
+ * The type of CONSTANT_InterfaceMethodref constant pool items.
+ */
+ static final int IMETH = 11;
+
+ /**
+ * The type of CONSTANT_String constant pool items.
+ */
+ static final int STR = 8;
+
+ /**
+ * The type of CONSTANT_Integer constant pool items.
+ */
+ static final int INT = 3;
+
+ /**
+ * The type of CONSTANT_Float constant pool items.
+ */
+ static final int FLOAT = 4;
+
+ /**
+ * The type of CONSTANT_Long constant pool items.
+ */
+ static final int LONG = 5;
+
+ /**
+ * The type of CONSTANT_Double constant pool items.
+ */
+ static final int DOUBLE = 6;
+
+ /**
+ * The type of CONSTANT_NameAndType constant pool items.
+ */
+ static final int NAME_TYPE = 12;
+
+ /**
+ * The type of CONSTANT_Utf8 constant pool items.
+ */
+ static final int UTF8 = 1;
+
+ /**
+ * Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable},
+ * instead of the constant pool, in order to avoid clashes with normal
+ * constant pool items in the ClassWriter constant pool's hash table.
+ */
+ static final int TYPE_NORMAL = 13;
+
+ /**
+ * Uninitialized type Item stored in the ClassWriter
+ * {@link ClassWriter#typeTable}, instead of the constant pool, in order to
+ * avoid clashes with normal constant pool items in the ClassWriter constant
+ * pool's hash table.
+ */
+ static final int TYPE_UNINIT = 14;
+
+ /**
+ * Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable},
+ * instead of the constant pool, in order to avoid clashes with normal
+ * constant pool items in the ClassWriter constant pool's hash table.
+ */
+ static final int TYPE_MERGED = 15;
+
+ /**
+ * The class reader from which this class writer was constructed, if any.
+ */
+ ClassReader cr;
+
+ /**
+ * Minor and major version numbers of the class to be generated.
+ */
+ int version;
+
+ /**
+ * Index of the next item to be added in the constant pool.
+ */
+ int index;
+
+ /**
+ * The constant pool of this class.
+ */
+ final ByteVector pool;
+
+ /**
+ * The constant pool's hash table data.
+ */
+ Item[] items;
+
+ /**
+ * The threshold of the constant pool's hash table.
+ */
+ int threshold;
+
+ /**
+ * A reusable key used to look for items in the {@link #items} hash table.
+ */
+ final Item key;
+
+ /**
+ * A reusable key used to look for items in the {@link #items} hash table.
+ */
+ final Item key2;
+
+ /**
+ * A reusable key used to look for items in the {@link #items} hash table.
+ */
+ final Item key3;
+
+ /**
+ * A type table used to temporarily store internal names that will not
+ * necessarily be stored in the constant pool. This type table is used by
+ * the control flow and data flow analysis algorithm used to compute stack
+ * map frames from scratch. This array associates to each index <tt>i</tt>
+ * the Item whose index is <tt>i</tt>. All Item objects stored in this
+ * array are also stored in the {@link #items} hash table. These two arrays
+ * allow to retrieve an Item from its index or, conversely, to get the index
+ * of an Item from its value. Each Item stores an internal name in its
+ * {@link Item#strVal1} field.
+ */
+ Item[] typeTable;
+
+ /**
+ * Number of elements in the {@link #typeTable} array.
+ */
+ private short typeCount;
+
+ /**
+ * The access flags of this class.
+ */
+ private int access;
+
+ /**
+ * The constant pool item that contains the internal name of this class.
+ */
+ private int name;
+
+ /**
+ * The internal name of this class.
+ */
+ String thisName;
+
+ /**
+ * The constant pool item that contains the signature of this class.
+ */
+ private int signature;
+
+ /**
+ * The constant pool item that contains the internal name of the super class
+ * of this class.
+ */
+ private int superName;
+
+ /**
+ * Number of interfaces implemented or extended by this class or interface.
+ */
+ private int interfaceCount;
+
+ /**
+ * The interfaces implemented or extended by this class or interface. More
+ * precisely, this array contains the indexes of the constant pool items
+ * that contain the internal names of these interfaces.
+ */
+ private int[] interfaces;
+
+ /**
+ * The index of the constant pool item that contains the name of the source
+ * file from which this class was compiled.
+ */
+ private int sourceFile;
+
+ /**
+ * The SourceDebug attribute of this class.
+ */
+ private ByteVector sourceDebug;
+
+ /**
+ * The constant pool item that contains the name of the enclosing class of
+ * this class.
+ */
+ private int enclosingMethodOwner;
+
+ /**
+ * The constant pool item that contains the name and descriptor of the
+ * enclosing method of this class.
+ */
+ private int enclosingMethod;
+
+ /**
+ * The runtime visible annotations of this class.
+ */
+ private AnnotationWriter anns;
+
+ /**
+ * The runtime invisible annotations of this class.
+ */
+ private AnnotationWriter ianns;
+
+ /**
+ * The non standard attributes of this class.
+ */
+ private Attribute attrs;
+
+ /**
+ * The number of entries in the InnerClasses attribute.
+ */
+ private int innerClassesCount;
+
+ /**
+ * The InnerClasses attribute.
+ */
+ private ByteVector innerClasses;
+
+ /**
+ * The fields of this class. These fields are stored in a linked list of
+ * {@link FieldWriter} objects, linked to each other by their
+ * {@link FieldWriter#next} field. This field stores the first element of
+ * this list.
+ */
+ FieldWriter firstField;
+
+ /**
+ * The fields of this class. These fields are stored in a linked list of
+ * {@link FieldWriter} objects, linked to each other by their
+ * {@link FieldWriter#next} field. This field stores the last element of
+ * this list.
+ */
+ FieldWriter lastField;
+
+ /**
+ * The methods of this class. These methods are stored in a linked list of
+ * {@link MethodWriter} objects, linked to each other by their
+ * {@link MethodWriter#next} field. This field stores the first element of
+ * this list.
+ */
+ MethodWriter firstMethod;
+
+ /**
+ * The methods of this class. These methods are stored in a linked list of
+ * {@link MethodWriter} objects, linked to each other by their
+ * {@link MethodWriter#next} field. This field stores the last element of
+ * this list.
+ */
+ MethodWriter lastMethod;
+
+ /**
+ * <tt>true</tt> if the maximum stack size and number of local variables
+ * must be automatically computed.
+ */
+ private final boolean computeMaxs;
+
+ /**
+ * <tt>true</tt> if the stack map frames must be recomputed from scratch.
+ */
+ private final boolean computeFrames;
+
+ /**
+ * <tt>true</tt> if the stack map tables of this class are invalid. The
+ * {@link MethodWriter#resizeInstructions} method cannot transform existing
+ * stack map tables, and so produces potentially invalid classes when it is
+ * executed. In this case the class is reread and rewritten with the
+ * {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize
+ * stack map tables when this option is used).
+ */
+ boolean invalidFrames;
+
+ // ------------------------------------------------------------------------
+ // Static initializer
+ // ------------------------------------------------------------------------
+
+ /**
+ * Computes the instruction types of JVM opcodes.
+ */
+ static {
+ int i;
+ byte[] b = new byte[220];
+ String s = "AAAAAAAAAAAAAAAABCKLLDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
+ + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ + "AAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAIIIIIIIIIIIIIIIIDNOAA"
+ + "AAAAGGGGGGGHAFBFAAFFAAQPIIJJIIIIIIIIIIIIIIIIII";
+ for (i = 0; i < b.length; ++i) {
+ b[i] = (byte) (s.charAt(i) - 'A');
+ }
+ TYPE = b;
+
+ // code to generate the above string
+ //
+ // // SBYTE_INSN instructions
+ // b[Constants.NEWARRAY] = SBYTE_INSN;
+ // b[Constants.BIPUSH] = SBYTE_INSN;
+ //
+ // // SHORT_INSN instructions
+ // b[Constants.SIPUSH] = SHORT_INSN;
+ //
+ // // (IMPL)VAR_INSN instructions
+ // b[Constants.RET] = VAR_INSN;
+ // for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) {
+ // b[i] = VAR_INSN;
+ // }
+ // for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) {
+ // b[i] = VAR_INSN;
+ // }
+ // for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3
+ // b[i] = IMPLVAR_INSN;
+ // }
+ // for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3
+ // b[i] = IMPLVAR_INSN;
+ // }
+ //
+ // // TYPE_INSN instructions
+ // b[Constants.NEW] = TYPE_INSN;
+ // b[Constants.ANEWARRAY] = TYPE_INSN;
+ // b[Constants.CHECKCAST] = TYPE_INSN;
+ // b[Constants.INSTANCEOF] = TYPE_INSN;
+ //
+ // // (Set)FIELDORMETH_INSN instructions
+ // for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) {
+ // b[i] = FIELDORMETH_INSN;
+ // }
+ // b[Constants.INVOKEINTERFACE] = ITFMETH_INSN;
+ //
+ // // LABEL(W)_INSN instructions
+ // for (i = Constants.IFEQ; i <= Constants.JSR; ++i) {
+ // b[i] = LABEL_INSN;
+ // }
+ // b[Constants.IFNULL] = LABEL_INSN;
+ // b[Constants.IFNONNULL] = LABEL_INSN;
+ // b[200] = LABELW_INSN; // GOTO_W
+ // b[201] = LABELW_INSN; // JSR_W
+ // // temporary opcodes used internally by ASM - see Label and
+ // MethodWriter
+ // for (i = 202; i < 220; ++i) {
+ // b[i] = LABEL_INSN;
+ // }
+ //
+ // // LDC(_W) instructions
+ // b[Constants.LDC] = LDC_INSN;
+ // b[19] = LDCW_INSN; // LDC_W
+ // b[20] = LDCW_INSN; // LDC2_W
+ //
+ // // special instructions
+ // b[Constants.IINC] = IINC_INSN;
+ // b[Constants.TABLESWITCH] = TABL_INSN;
+ // b[Constants.LOOKUPSWITCH] = LOOK_INSN;
+ // b[Constants.MULTIANEWARRAY] = MANA_INSN;
+ // b[196] = WIDE_INSN; // WIDE
+ //
+ // for (i = 0; i < b.length; ++i) {
+ // System.err.print((char)('A' + b[i]));
+ // }
+ // System.err.println();
+ }
+
+ // ------------------------------------------------------------------------
+ // Constructor
+ // ------------------------------------------------------------------------
+
+ /**
+ * Constructs a new {@link ClassWriter} object.
+ *
+ * @param flags option flags that can be used to modify the default behavior
+ * of this class. See {@link #COMPUTE_MAXS}, {@link #COMPUTE_FRAMES}.
+ */
+ public ClassWriter(final int flags) {
+ index = 1;
+ pool = new ByteVector();
+ items = new Item[256];
+ threshold = (int) (0.75d * items.length);
+ key = new Item();
+ key2 = new Item();
+ key3 = new Item();
+ this.computeMaxs = (flags & COMPUTE_MAXS) != 0;
+ this.computeFrames = (flags & COMPUTE_FRAMES) != 0;
+ }
+
+ /**
+ * Constructs a new {@link ClassWriter} object and enables optimizations for
+ * "mostly add" bytecode transformations. These optimizations are the
+ * following:
+ *
+ * <ul> <li>The constant pool from the original class is copied as is in
+ * the new class, which saves time. New constant pool entries will be added
+ * at the end if necessary, but unused constant pool entries <i>won't be
+ * removed</i>.</li> <li>Methods that are not transformed are copied as
+ * is in the new class, directly from the original class bytecode (i.e.
+ * without emitting visit events for all the method instructions), which
+ * saves a <i>lot</i> of time. Untransformed methods are detected by the
+ * fact that the {@link ClassReader} receives {@link MethodVisitor} objects
+ * that come from a {@link ClassWriter} (and not from a custom
+ * {@link ClassAdapter} or any other {@link ClassVisitor} instance).</li>
+ * </ul>
+ *
+ * @param classReader the {@link ClassReader} used to read the original
+ * class. It will be used to copy the entire constant pool from the
+ * original class and also to copy other fragments of original
+ * bytecode where applicable.
+ * @param flags option flags that can be used to modify the default behavior
+ * of this class. See {@link #COMPUTE_MAXS}, {@link #COMPUTE_FRAMES}.
+ */
+ public ClassWriter(final ClassReader classReader, final int flags) {
+ this(flags);
+ classReader.copyPool(this);
+ this.cr = classReader;
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the ClassVisitor interface
+ // ------------------------------------------------------------------------
+
+ public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces)
+ {
+ this.version = version;
+ this.access = access;
+ this.name = newClass(name);
+ thisName = name;
+ if (ClassReader.SIGNATURES && signature != null) {
+ this.signature = newUTF8(signature);
+ }
+ this.superName = superName == null ? 0 : newClass(superName);
+ if (interfaces != null && interfaces.length > 0) {
+ interfaceCount = interfaces.length;
+ this.interfaces = new int[interfaceCount];
+ for (int i = 0; i < interfaceCount; ++i) {
+ this.interfaces[i] = newClass(interfaces[i]);
+ }
+ }
+ }
+
+ public void visitSource(final String file, final String debug) {
+ if (file != null) {
+ sourceFile = newUTF8(file);
+ }
+ if (debug != null) {
+ sourceDebug = new ByteVector().putUTF8(debug);
+ }
+ }
+
+ public void visitOuterClass(
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ enclosingMethodOwner = newClass(owner);
+ if (name != null && desc != null) {
+ enclosingMethod = newNameType(name, desc);
+ }
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ if (!ClassReader.ANNOTATIONS) {
+ return null;
+ }
+ ByteVector bv = new ByteVector();
+ // write type, and reserve space for values count
+ bv.putShort(newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2);
+ if (visible) {
+ aw.next = anns;
+ anns = aw;
+ } else {
+ aw.next = ianns;
+ ianns = aw;
+ }
+ return aw;
+ }
+
+ public void visitAttribute(final Attribute attr) {
+ attr.next = attrs;
+ attrs = attr;
+ }
+
+ public void visitInnerClass(
+ final String name,
+ final String outerName,
+ final String innerName,
+ final int access)
+ {
+ if (innerClasses == null) {
+ innerClasses = new ByteVector();
+ }
+ ++innerClassesCount;
+ innerClasses.putShort(name == null ? 0 : newClass(name));
+ innerClasses.putShort(outerName == null ? 0 : newClass(outerName));
+ innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName));
+ innerClasses.putShort(access);
+ }
+
+ public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value)
+ {
+ return new FieldWriter(this, access, name, desc, signature, value);
+ }
+
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions)
+ {
+ return new MethodWriter(this,
+ access,
+ name,
+ desc,
+ signature,
+ exceptions,
+ computeMaxs,
+ computeFrames);
+ }
+
+ public void visitEnd() {
+ }
+
+ // ------------------------------------------------------------------------
+ // Other public methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the bytecode of the class that was build with this class writer.
+ *
+ * @return the bytecode of the class that was build with this class writer.
+ */
+ public byte[] toByteArray() {
+ // computes the real size of the bytecode of this class
+ int size = 24 + 2 * interfaceCount;
+ int nbFields = 0;
+ FieldWriter fb = firstField;
+ while (fb != null) {
+ ++nbFields;
+ size += fb.getSize();
+ fb = fb.next;
+ }
+ int nbMethods = 0;
+ MethodWriter mb = firstMethod;
+ while (mb != null) {
+ ++nbMethods;
+ size += mb.getSize();
+ mb = mb.next;
+ }
+ int attributeCount = 0;
+ if (ClassReader.SIGNATURES && signature != 0) {
+ ++attributeCount;
+ size += 8;
+ newUTF8("Signature");
+ }
+ if (sourceFile != 0) {
+ ++attributeCount;
+ size += 8;
+ newUTF8("SourceFile");
+ }
+ if (sourceDebug != null) {
+ ++attributeCount;
+ size += sourceDebug.length + 4;
+ newUTF8("SourceDebugExtension");
+ }
+ if (enclosingMethodOwner != 0) {
+ ++attributeCount;
+ size += 10;
+ newUTF8("EnclosingMethod");
+ }
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ ++attributeCount;
+ size += 6;
+ newUTF8("Deprecated");
+ }
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0
+ && (version & 0xffff) < Opcodes.V1_5)
+ {
+ ++attributeCount;
+ size += 6;
+ newUTF8("Synthetic");
+ }
+ if (innerClasses != null) {
+ ++attributeCount;
+ size += 8 + innerClasses.length;
+ newUTF8("InnerClasses");
+ }
+ if (ClassReader.ANNOTATIONS && anns != null) {
+ ++attributeCount;
+ size += 8 + anns.getSize();
+ newUTF8("RuntimeVisibleAnnotations");
+ }
+ if (ClassReader.ANNOTATIONS && ianns != null) {
+ ++attributeCount;
+ size += 8 + ianns.getSize();
+ newUTF8("RuntimeInvisibleAnnotations");
+ }
+ if (attrs != null) {
+ attributeCount += attrs.getCount();
+ size += attrs.getSize(this, null, 0, -1, -1);
+ }
+ size += pool.length;
+ // allocates a byte vector of this size, in order to avoid unnecessary
+ // arraycopy operations in the ByteVector.enlarge() method
+ ByteVector out = new ByteVector(size);
+ out.putInt(0xCAFEBABE).putInt(version);
+ out.putShort(index).putByteArray(pool.data, 0, pool.length);
+ out.putShort(access).putShort(name).putShort(superName);
+ out.putShort(interfaceCount);
+ for (int i = 0; i < interfaceCount; ++i) {
+ out.putShort(interfaces[i]);
+ }
+ out.putShort(nbFields);
+ fb = firstField;
+ while (fb != null) {
+ fb.put(out);
+ fb = fb.next;
+ }
+ out.putShort(nbMethods);
+ mb = firstMethod;
+ while (mb != null) {
+ mb.put(out);
+ mb = mb.next;
+ }
+ out.putShort(attributeCount);
+ if (ClassReader.SIGNATURES && signature != 0) {
+ out.putShort(newUTF8("Signature")).putInt(2).putShort(signature);
+ }
+ if (sourceFile != 0) {
+ out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile);
+ }
+ if (sourceDebug != null) {
+ int len = sourceDebug.length - 2;
+ out.putShort(newUTF8("SourceDebugExtension")).putInt(len);
+ out.putByteArray(sourceDebug.data, 2, len);
+ }
+ if (enclosingMethodOwner != 0) {
+ out.putShort(newUTF8("EnclosingMethod")).putInt(4);
+ out.putShort(enclosingMethodOwner).putShort(enclosingMethod);
+ }
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ out.putShort(newUTF8("Deprecated")).putInt(0);
+ }
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0
+ && (version & 0xffff) < Opcodes.V1_5)
+ {
+ out.putShort(newUTF8("Synthetic")).putInt(0);
+ }
+ if (innerClasses != null) {
+ out.putShort(newUTF8("InnerClasses"));
+ out.putInt(innerClasses.length + 2).putShort(innerClassesCount);
+ out.putByteArray(innerClasses.data, 0, innerClasses.length);
+ }
+ if (ClassReader.ANNOTATIONS && anns != null) {
+ out.putShort(newUTF8("RuntimeVisibleAnnotations"));
+ anns.put(out);
+ }
+ if (ClassReader.ANNOTATIONS && ianns != null) {
+ out.putShort(newUTF8("RuntimeInvisibleAnnotations"));
+ ianns.put(out);
+ }
+ if (attrs != null) {
+ attrs.put(this, null, 0, -1, -1, out);
+ }
+ if (invalidFrames) {
+ ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
+ new ClassReader(out.data).accept(cw, ClassReader.SKIP_FRAMES);
+ return cw.toByteArray();
+ }
+ return out.data;
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods: constant pool management
+ // ------------------------------------------------------------------------
+
+ /**
+ * Adds a number or string constant to the constant pool of the class being
+ * build. Does nothing if the constant pool already contains a similar item.
+ *
+ * @param cst the value of the constant to be added to the constant pool.
+ * This parameter must be an {@link Integer}, a {@link Float}, a
+ * {@link Long}, a {@link Double}, a {@link String} or a
+ * {@link Type}.
+ * @return a new or already existing constant item with the given value.
+ */
+ Item newConstItem(final Object cst) {
+ if (cst instanceof Integer) {
+ int val = ((Integer) cst).intValue();
+ return newInteger(val);
+ } else if (cst instanceof Byte) {
+ int val = ((Byte) cst).intValue();
+ return newInteger(val);
+ } else if (cst instanceof Character) {
+ int val = ((Character) cst).charValue();
+ return newInteger(val);
+ } else if (cst instanceof Short) {
+ int val = ((Short) cst).intValue();
+ return newInteger(val);
+ } else if (cst instanceof Boolean) {
+ int val = ((Boolean) cst).booleanValue() ? 1 : 0;
+ return newInteger(val);
+ } else if (cst instanceof Float) {
+ float val = ((Float) cst).floatValue();
+ return newFloat(val);
+ } else if (cst instanceof Long) {
+ long val = ((Long) cst).longValue();
+ return newLong(val);
+ } else if (cst instanceof Double) {
+ double val = ((Double) cst).doubleValue();
+ return newDouble(val);
+ } else if (cst instanceof String) {
+ return newString((String) cst);
+ } else if (cst instanceof Type) {
+ Type t = (Type) cst;
+ return newClassItem(t.getSort() == Type.OBJECT
+ ? t.getInternalName()
+ : t.getDescriptor());
+ } else {
+ throw new IllegalArgumentException("value " + cst);
+ }
+ }
+
+ /**
+ * Adds a number or string constant to the constant pool of the class being
+ * build. Does nothing if the constant pool already contains a similar item.
+ * <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @param cst the value of the constant to be added to the constant pool.
+ * This parameter must be an {@link Integer}, a {@link Float}, a
+ * {@link Long}, a {@link Double} or a {@link String}.
+ * @return the index of a new or already existing constant item with the
+ * given value.
+ */
+ public int newConst(final Object cst) {
+ return newConstItem(cst).index;
+ }
+
+ /**
+ * Adds an UTF8 string to the constant pool of the class being build. Does
+ * nothing if the constant pool already contains a similar item. <i>This
+ * method is intended for {@link Attribute} sub classes, and is normally not
+ * needed by class generators or adapters.</i>
+ *
+ * @param value the String value.
+ * @return the index of a new or already existing UTF8 item.
+ */
+ public int newUTF8(final String value) {
+ key.set(UTF8, value, null, null);
+ Item result = get(key);
+ if (result == null) {
+ pool.putByte(UTF8).putUTF8(value);
+ result = new Item(index++, key);
+ put(result);
+ }
+ return result.index;
+ }
+
+ /**
+ * Adds a class reference to the constant pool of the class being build.
+ * Does nothing if the constant pool already contains a similar item.
+ * <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @param value the internal name of the class.
+ * @return a new or already existing class reference item.
+ */
+ Item newClassItem(final String value) {
+ key2.set(CLASS, value, null, null);
+ Item result = get(key2);
+ if (result == null) {
+ pool.put12(CLASS, newUTF8(value));
+ result = new Item(index++, key2);
+ put(result);
+ }
+ return result;
+ }
+
+ /**
+ * Adds a class reference to the constant pool of the class being build.
+ * Does nothing if the constant pool already contains a similar item.
+ * <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @param value the internal name of the class.
+ * @return the index of a new or already existing class reference item.
+ */
+ public int newClass(final String value) {
+ return newClassItem(value).index;
+ }
+
+ /**
+ * Adds a field reference to the constant pool of the class being build.
+ * Does nothing if the constant pool already contains a similar item.
+ *
+ * @param owner the internal name of the field's owner class.
+ * @param name the field's name.
+ * @param desc the field's descriptor.
+ * @return a new or already existing field reference item.
+ */
+ Item newFieldItem(final String owner, final String name, final String desc)
+ {
+ key3.set(FIELD, owner, name, desc);
+ Item result = get(key3);
+ if (result == null) {
+ put122(FIELD, newClass(owner), newNameType(name, desc));
+ result = new Item(index++, key3);
+ put(result);
+ }
+ return result;
+ }
+
+ /**
+ * Adds a field reference to the constant pool of the class being build.
+ * Does nothing if the constant pool already contains a similar item.
+ * <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @param owner the internal name of the field's owner class.
+ * @param name the field's name.
+ * @param desc the field's descriptor.
+ * @return the index of a new or already existing field reference item.
+ */
+ public int newField(final String owner, final String name, final String desc)
+ {
+ return newFieldItem(owner, name, desc).index;
+ }
+
+ /**
+ * Adds a method reference to the constant pool of the class being build.
+ * Does nothing if the constant pool already contains a similar item.
+ *
+ * @param owner the internal name of the method's owner class.
+ * @param name the method's name.
+ * @param desc the method's descriptor.
+ * @param itf <tt>true</tt> if <tt>owner</tt> is an interface.
+ * @return a new or already existing method reference item.
+ */
+ Item newMethodItem(
+ final String owner,
+ final String name,
+ final String desc,
+ final boolean itf)
+ {
+ int type = itf ? IMETH : METH;
+ key3.set(type, owner, name, desc);
+ Item result = get(key3);
+ if (result == null) {
+ put122(type, newClass(owner), newNameType(name, desc));
+ result = new Item(index++, key3);
+ put(result);
+ }
+ return result;
+ }
+
+ /**
+ * Adds a method reference to the constant pool of the class being build.
+ * Does nothing if the constant pool already contains a similar item.
+ * <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @param owner the internal name of the method's owner class.
+ * @param name the method's name.
+ * @param desc the method's descriptor.
+ * @param itf <tt>true</tt> if <tt>owner</tt> is an interface.
+ * @return the index of a new or already existing method reference item.
+ */
+ public int newMethod(
+ final String owner,
+ final String name,
+ final String desc,
+ final boolean itf)
+ {
+ return newMethodItem(owner, name, desc, itf).index;
+ }
+
+ /**
+ * Adds an integer to the constant pool of the class being build. Does
+ * nothing if the constant pool already contains a similar item.
+ *
+ * @param value the int value.
+ * @return a new or already existing int item.
+ */
+ Item newInteger(final int value) {
+ key.set(value);
+ Item result = get(key);
+ if (result == null) {
+ pool.putByte(INT).putInt(value);
+ result = new Item(index++, key);
+ put(result);
+ }
+ return result;
+ }
+
+ /**
+ * Adds a float to the constant pool of the class being build. Does nothing
+ * if the constant pool already contains a similar item.
+ *
+ * @param value the float value.
+ * @return a new or already existing float item.
+ */
+ Item newFloat(final float value) {
+ key.set(value);
+ Item result = get(key);
+ if (result == null) {
+ pool.putByte(FLOAT).putInt(key.intVal);
+ result = new Item(index++, key);
+ put(result);
+ }
+ return result;
+ }
+
+ /**
+ * Adds a long to the constant pool of the class being build. Does nothing
+ * if the constant pool already contains a similar item.
+ *
+ * @param value the long value.
+ * @return a new or already existing long item.
+ */
+ Item newLong(final long value) {
+ key.set(value);
+ Item result = get(key);
+ if (result == null) {
+ pool.putByte(LONG).putLong(value);
+ result = new Item(index, key);
+ put(result);
+ index += 2;
+ }
+ return result;
+ }
+
+ /**
+ * Adds a double to the constant pool of the class being build. Does nothing
+ * if the constant pool already contains a similar item.
+ *
+ * @param value the double value.
+ * @return a new or already existing double item.
+ */
+ Item newDouble(final double value) {
+ key.set(value);
+ Item result = get(key);
+ if (result == null) {
+ pool.putByte(DOUBLE).putLong(key.longVal);
+ result = new Item(index, key);
+ put(result);
+ index += 2;
+ }
+ return result;
+ }
+
+ /**
+ * Adds a string to the constant pool of the class being build. Does nothing
+ * if the constant pool already contains a similar item.
+ *
+ * @param value the String value.
+ * @return a new or already existing string item.
+ */
+ private Item newString(final String value) {
+ key2.set(STR, value, null, null);
+ Item result = get(key2);
+ if (result == null) {
+ pool.put12(STR, newUTF8(value));
+ result = new Item(index++, key2);
+ put(result);
+ }
+ return result;
+ }
+
+ /**
+ * Adds a name and type to the constant pool of the class being build. Does
+ * nothing if the constant pool already contains a similar item. <i>This
+ * method is intended for {@link Attribute} sub classes, and is normally not
+ * needed by class generators or adapters.</i>
+ *
+ * @param name a name.
+ * @param desc a type descriptor.
+ * @return the index of a new or already existing name and type item.
+ */
+ public int newNameType(final String name, final String desc) {
+ key2.set(NAME_TYPE, name, desc, null);
+ Item result = get(key2);
+ if (result == null) {
+ put122(NAME_TYPE, newUTF8(name), newUTF8(desc));
+ result = new Item(index++, key2);
+ put(result);
+ }
+ return result.index;
+ }
+
+ /**
+ * Adds the given internal name to {@link #typeTable} and returns its index.
+ * Does nothing if the type table already contains this internal name.
+ *
+ * @param type the internal name to be added to the type table.
+ * @return the index of this internal name in the type table.
+ */
+ int addType(final String type) {
+ key.set(TYPE_NORMAL, type, null, null);
+ Item result = get(key);
+ if (result == null) {
+ result = addType(key);
+ }
+ return result.index;
+ }
+
+ /**
+ * Adds the given "uninitialized" type to {@link #typeTable} and returns its
+ * index. This method is used for UNINITIALIZED types, made of an internal
+ * name and a bytecode offset.
+ *
+ * @param type the internal name to be added to the type table.
+ * @param offset the bytecode offset of the NEW instruction that created
+ * this UNINITIALIZED type value.
+ * @return the index of this internal name in the type table.
+ */
+ int addUninitializedType(final String type, final int offset) {
+ key.type = TYPE_UNINIT;
+ key.intVal = offset;
+ key.strVal1 = type;
+ key.hashCode = 0x7FFFFFFF & (TYPE_UNINIT + type.hashCode() + offset);
+ Item result = get(key);
+ if (result == null) {
+ result = addType(key);
+ }
+ return result.index;
+ }
+
+ /**
+ * Adds the given Item to {@link #typeTable}.
+ *
+ * @param item the value to be added to the type table.
+ * @return the added Item, which a new Item instance with the same value as
+ * the given Item.
+ */
+ private Item addType(final Item item) {
+ ++typeCount;
+ Item result = new Item(typeCount, key);
+ put(result);
+ if (typeTable == null) {
+ typeTable = new Item[16];
+ }
+ if (typeCount == typeTable.length) {
+ Item[] newTable = new Item[2 * typeTable.length];
+ System.arraycopy(typeTable, 0, newTable, 0, typeTable.length);
+ typeTable = newTable;
+ }
+ typeTable[typeCount] = result;
+ return result;
+ }
+
+ /**
+ * Returns the index of the common super type of the two given types. This
+ * method calls {@link #getCommonSuperClass} and caches the result in the
+ * {@link #items} hash table to speedup future calls with the same
+ * parameters.
+ *
+ * @param type1 index of an internal name in {@link #typeTable}.
+ * @param type2 index of an internal name in {@link #typeTable}.
+ * @return the index of the common super type of the two given types.
+ */
+ int getMergedType(final int type1, final int type2) {
+ key2.type = TYPE_MERGED;
+ key2.longVal = type1 | (((long) type2) << 32);
+ key2.hashCode = 0x7FFFFFFF & (TYPE_MERGED + type1 + type2);
+ Item result = get(key2);
+ if (result == null) {
+ String t = typeTable[type1].strVal1;
+ String u = typeTable[type2].strVal1;
+ key2.intVal = addType(getCommonSuperClass(t, u));
+ result = new Item((short) 0, key2);
+ put(result);
+ }
+ return result.intVal;
+ }
+
+ /**
+ * Returns the common super type of the two given types. The default
+ * implementation of this method <i>loads<i> the two given classes and uses
+ * the java.lang.Class methods to find the common super class. It can be
+ * overridden to compute this common super type in other ways, in particular
+ * without actually loading any class, or to take into account the class
+ * that is currently being generated by this ClassWriter, which can of
+ * course not be loaded since it is under construction.
+ *
+ * @param type1 the internal name of a class.
+ * @param type2 the internal name of another class.
+ * @return the internal name of the common super class of the two given
+ * classes.
+ */
+ protected String getCommonSuperClass(final String type1, final String type2)
+ {
+ Class c, d;
+ try {
+ c = Class.forName(type1.replace('/', '.'));
+ d = Class.forName(type2.replace('/', '.'));
+ } catch (Exception e) {
+ throw new RuntimeException(e.toString());
+ }
+ if (c.isAssignableFrom(d)) {
+ return type1;
+ }
+ if (d.isAssignableFrom(c)) {
+ return type2;
+ }
+ if (c.isInterface() || d.isInterface()) {
+ return "java/lang/Object";
+ } else {
+ do {
+ c = c.getSuperclass();
+ } while (!c.isAssignableFrom(d));
+ return c.getName().replace('.', '/');
+ }
+ }
+
+ /**
+ * Returns the constant pool's hash table item which is equal to the given
+ * item.
+ *
+ * @param key a constant pool item.
+ * @return the constant pool's hash table item which is equal to the given
+ * item, or <tt>null</tt> if there is no such item.
+ */
+ private Item get(final Item key) {
+ Item i = items[key.hashCode % items.length];
+ while (i != null && !key.isEqualTo(i)) {
+ i = i.next;
+ }
+ return i;
+ }
+
+ /**
+ * Puts the given item in the constant pool's hash table. The hash table
+ * <i>must</i> not already contains this item.
+ *
+ * @param i the item to be added to the constant pool's hash table.
+ */
+ private void put(final Item i) {
+ if (index > threshold) {
+ int ll = items.length;
+ int nl = ll * 2 + 1;
+ Item[] newItems = new Item[nl];
+ for (int l = ll - 1; l >= 0; --l) {
+ Item j = items[l];
+ while (j != null) {
+ int index = j.hashCode % newItems.length;
+ Item k = j.next;
+ j.next = newItems[index];
+ newItems[index] = j;
+ j = k;
+ }
+ }
+ items = newItems;
+ threshold = (int) (nl * 0.75);
+ }
+ int index = i.hashCode % items.length;
+ i.next = items[index];
+ items[index] = i;
+ }
+
+ /**
+ * Puts one byte and two shorts into the constant pool.
+ *
+ * @param b a byte.
+ * @param s1 a short.
+ * @param s2 another short.
+ */
+ private void put122(final int b, final int s1, final int s2) {
+ pool.put12(b, s1).putShort(s2);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/Edge.java b/cglib-and-asm/src/org/mockito/asm/Edge.java
new file mode 100644
index 0000000..587f334
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/Edge.java
@@ -0,0 +1,75 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * An edge in the control flow graph of a method body. See {@link Label Label}.
+ *
+ * @author Eric Bruneton
+ */
+class Edge {
+
+ /**
+ * Denotes a normal control flow graph edge.
+ */
+ static final int NORMAL = 0;
+
+ /**
+ * Denotes a control flow graph edge corresponding to an exception handler.
+ * More precisely any {@link Edge} whose {@link #info} is strictly positive
+ * corresponds to an exception handler. The actual value of {@link #info} is
+ * the index, in the {@link ClassWriter} type table, of the exception that
+ * is catched.
+ */
+ static final int EXCEPTION = 0x7FFFFFFF;
+
+ /**
+ * Information about this control flow graph edge. If
+ * {@link ClassWriter#COMPUTE_MAXS} is used this field is the (relative)
+ * stack size in the basic block from which this edge originates. This size
+ * is equal to the stack size at the "jump" instruction to which this edge
+ * corresponds, relatively to the stack size at the beginning of the
+ * originating basic block. If {@link ClassWriter#COMPUTE_FRAMES} is used,
+ * this field is the kind of this control flow graph edge (i.e. NORMAL or
+ * EXCEPTION).
+ */
+ int info;
+
+ /**
+ * The successor block of the basic block from which this edge originates.
+ */
+ Label successor;
+
+ /**
+ * The next edge in the list of successors of the originating basic block.
+ * See {@link Label#successors successors}.
+ */
+ Edge next;
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/FieldVisitor.java b/cglib-and-asm/src/org/mockito/asm/FieldVisitor.java
new file mode 100644
index 0000000..24c18b1
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/FieldVisitor.java
@@ -0,0 +1,64 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * A visitor to visit a Java field. The methods of this interface must be called
+ * in the following order: ( <tt>visitAnnotation</tt> |
+ * <tt>visitAttribute</tt> )* <tt>visitEnd</tt>.
+ *
+ * @author Eric Bruneton
+ */
+public interface FieldVisitor {
+
+ /**
+ * Visits an annotation of the field.
+ *
+ * @param desc the class descriptor of the annotation class.
+ * @param visible <tt>true</tt> if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or <tt>null</tt> if
+ * this visitor is not interested in visiting this annotation.
+ */
+ AnnotationVisitor visitAnnotation(String desc, boolean visible);
+
+ /**
+ * Visits a non standard attribute of the field.
+ *
+ * @param attr an attribute.
+ */
+ void visitAttribute(Attribute attr);
+
+ /**
+ * Visits the end of the field. This method, which is the last one to be
+ * called, is used to inform the visitor that all the annotations and
+ * attributes of the field have been visited.
+ */
+ void visitEnd();
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/FieldWriter.java b/cglib-and-asm/src/org/mockito/asm/FieldWriter.java
new file mode 100644
index 0000000..99db9a3
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/FieldWriter.java
@@ -0,0 +1,269 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * An {@link FieldVisitor} that generates Java fields in bytecode form.
+ *
+ * @author Eric Bruneton
+ */
+final class FieldWriter implements FieldVisitor {
+
+ /**
+ * Next field writer (see {@link ClassWriter#firstField firstField}).
+ */
+ FieldWriter next;
+
+ /**
+ * The class writer to which this field must be added.
+ */
+ private final ClassWriter cw;
+
+ /**
+ * Access flags of this field.
+ */
+ private final int access;
+
+ /**
+ * The index of the constant pool item that contains the name of this
+ * method.
+ */
+ private final int name;
+
+ /**
+ * The index of the constant pool item that contains the descriptor of this
+ * field.
+ */
+ private final int desc;
+
+ /**
+ * The index of the constant pool item that contains the signature of this
+ * field.
+ */
+ private int signature;
+
+ /**
+ * The index of the constant pool item that contains the constant value of
+ * this field.
+ */
+ private int value;
+
+ /**
+ * The runtime visible annotations of this field. May be <tt>null</tt>.
+ */
+ private AnnotationWriter anns;
+
+ /**
+ * The runtime invisible annotations of this field. May be <tt>null</tt>.
+ */
+ private AnnotationWriter ianns;
+
+ /**
+ * The non standard attributes of this field. May be <tt>null</tt>.
+ */
+ private Attribute attrs;
+
+ // ------------------------------------------------------------------------
+ // Constructor
+ // ------------------------------------------------------------------------
+
+ /**
+ * Constructs a new {@link FieldWriter}.
+ *
+ * @param cw the class writer to which this field must be added.
+ * @param access the field's access flags (see {@link Opcodes}).
+ * @param name the field's name.
+ * @param desc the field's descriptor (see {@link Type}).
+ * @param signature the field's signature. May be <tt>null</tt>.
+ * @param value the field's constant value. May be <tt>null</tt>.
+ */
+ FieldWriter(
+ final ClassWriter cw,
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value)
+ {
+ if (cw.firstField == null) {
+ cw.firstField = this;
+ } else {
+ cw.lastField.next = this;
+ }
+ cw.lastField = this;
+ this.cw = cw;
+ this.access = access;
+ this.name = cw.newUTF8(name);
+ this.desc = cw.newUTF8(desc);
+ if (ClassReader.SIGNATURES && signature != null) {
+ this.signature = cw.newUTF8(signature);
+ }
+ if (value != null) {
+ this.value = cw.newConstItem(value).index;
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the FieldVisitor interface
+ // ------------------------------------------------------------------------
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ if (!ClassReader.ANNOTATIONS) {
+ return null;
+ }
+ ByteVector bv = new ByteVector();
+ // write type, and reserve space for values count
+ bv.putShort(cw.newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
+ if (visible) {
+ aw.next = anns;
+ anns = aw;
+ } else {
+ aw.next = ianns;
+ ianns = aw;
+ }
+ return aw;
+ }
+
+ public void visitAttribute(final Attribute attr) {
+ attr.next = attrs;
+ attrs = attr;
+ }
+
+ public void visitEnd() {
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the size of this field.
+ *
+ * @return the size of this field.
+ */
+ int getSize() {
+ int size = 8;
+ if (value != 0) {
+ cw.newUTF8("ConstantValue");
+ size += 8;
+ }
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ cw.newUTF8("Synthetic");
+ size += 6;
+ }
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ cw.newUTF8("Deprecated");
+ size += 6;
+ }
+ if (ClassReader.SIGNATURES && signature != 0) {
+ cw.newUTF8("Signature");
+ size += 8;
+ }
+ if (ClassReader.ANNOTATIONS && anns != null) {
+ cw.newUTF8("RuntimeVisibleAnnotations");
+ size += 8 + anns.getSize();
+ }
+ if (ClassReader.ANNOTATIONS && ianns != null) {
+ cw.newUTF8("RuntimeInvisibleAnnotations");
+ size += 8 + ianns.getSize();
+ }
+ if (attrs != null) {
+ size += attrs.getSize(cw, null, 0, -1, -1);
+ }
+ return size;
+ }
+
+ /**
+ * Puts the content of this field into the given byte vector.
+ *
+ * @param out where the content of this field must be put.
+ */
+ void put(final ByteVector out) {
+ out.putShort(access).putShort(name).putShort(desc);
+ int attributeCount = 0;
+ if (value != 0) {
+ ++attributeCount;
+ }
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ ++attributeCount;
+ }
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ ++attributeCount;
+ }
+ if (ClassReader.SIGNATURES && signature != 0) {
+ ++attributeCount;
+ }
+ if (ClassReader.ANNOTATIONS && anns != null) {
+ ++attributeCount;
+ }
+ if (ClassReader.ANNOTATIONS && ianns != null) {
+ ++attributeCount;
+ }
+ if (attrs != null) {
+ attributeCount += attrs.getCount();
+ }
+ out.putShort(attributeCount);
+ if (value != 0) {
+ out.putShort(cw.newUTF8("ConstantValue"));
+ out.putInt(2).putShort(value);
+ }
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ out.putShort(cw.newUTF8("Synthetic")).putInt(0);
+ }
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ out.putShort(cw.newUTF8("Deprecated")).putInt(0);
+ }
+ if (ClassReader.SIGNATURES && signature != 0) {
+ out.putShort(cw.newUTF8("Signature"));
+ out.putInt(2).putShort(signature);
+ }
+ if (ClassReader.ANNOTATIONS && anns != null) {
+ out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
+ anns.put(out);
+ }
+ if (ClassReader.ANNOTATIONS && ianns != null) {
+ out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
+ ianns.put(out);
+ }
+ if (attrs != null) {
+ attrs.put(cw, null, 0, -1, -1, out);
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/Frame.java b/cglib-and-asm/src/org/mockito/asm/Frame.java
new file mode 100644
index 0000000..d1a4532
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/Frame.java
@@ -0,0 +1,1402 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * Information about the input and output stack map frames of a basic block.
+ *
+ * @author Eric Bruneton
+ */
+final class Frame {
+
+ /*
+ * Frames are computed in a two steps process: during the visit of each
+ * instruction, the state of the frame at the end of current basic block is
+ * updated by simulating the action of the instruction on the previous state
+ * of this so called "output frame". In visitMaxs, a fix point algorithm is
+ * used to compute the "input frame" of each basic block, i.e. the stack map
+ * frame at the begining of the basic block, starting from the input frame
+ * of the first basic block (which is computed from the method descriptor),
+ * and by using the previously computed output frames to compute the input
+ * state of the other blocks.
+ *
+ * All output and input frames are stored as arrays of integers. Reference
+ * and array types are represented by an index into a type table (which is
+ * not the same as the constant pool of the class, in order to avoid adding
+ * unnecessary constants in the pool - not all computed frames will end up
+ * being stored in the stack map table). This allows very fast type
+ * comparisons.
+ *
+ * Output stack map frames are computed relatively to the input frame of the
+ * basic block, which is not yet known when output frames are computed. It
+ * is therefore necessary to be able to represent abstract types such as
+ * "the type at position x in the input frame locals" or "the type at
+ * position x from the top of the input frame stack" or even "the type at
+ * position x in the input frame, with y more (or less) array dimensions".
+ * This explains the rather complicated type format used in output frames.
+ *
+ * This format is the following: DIM KIND VALUE (4, 4 and 24 bits). DIM is a
+ * signed number of array dimensions (from -8 to 7). KIND is either BASE,
+ * LOCAL or STACK. BASE is used for types that are not relative to the input
+ * frame. LOCAL is used for types that are relative to the input local
+ * variable types. STACK is used for types that are relative to the input
+ * stack types. VALUE depends on KIND. For LOCAL types, it is an index in
+ * the input local variable types. For STACK types, it is a position
+ * relatively to the top of input frame stack. For BASE types, it is either
+ * one of the constants defined in FrameVisitor, or for OBJECT and
+ * UNINITIALIZED types, a tag and an index in the type table.
+ *
+ * Output frames can contain types of any kind and with a positive or
+ * negative dimension (and even unassigned types, represented by 0 - which
+ * does not correspond to any valid type value). Input frames can only
+ * contain BASE types of positive or null dimension. In all cases the type
+ * table contains only internal type names (array type descriptors are
+ * forbidden - dimensions must be represented through the DIM field).
+ *
+ * The LONG and DOUBLE types are always represented by using two slots (LONG +
+ * TOP or DOUBLE + TOP), for local variable types as well as in the operand
+ * stack. This is necessary to be able to simulate DUPx_y instructions,
+ * whose effect would be dependent on the actual type values if types were
+ * always represented by a single slot in the stack (and this is not
+ * possible, since actual type values are not always known - cf LOCAL and
+ * STACK type kinds).
+ */
+
+ /**
+ * Mask to get the dimension of a frame type. This dimension is a signed
+ * integer between -8 and 7.
+ */
+ static final int DIM = 0xF0000000;
+
+ /**
+ * Constant to be added to a type to get a type with one more dimension.
+ */
+ static final int ARRAY_OF = 0x10000000;
+
+ /**
+ * Constant to be added to a type to get a type with one less dimension.
+ */
+ static final int ELEMENT_OF = 0xF0000000;
+
+ /**
+ * Mask to get the kind of a frame type.
+ *
+ * @see #BASE
+ * @see #LOCAL
+ * @see #STACK
+ */
+ static final int KIND = 0xF000000;
+
+ /**
+ * Mask to get the value of a frame type.
+ */
+ static final int VALUE = 0xFFFFFF;
+
+ /**
+ * Mask to get the kind of base types.
+ */
+ static final int BASE_KIND = 0xFF00000;
+
+ /**
+ * Mask to get the value of base types.
+ */
+ static final int BASE_VALUE = 0xFFFFF;
+
+ /**
+ * Kind of the types that are not relative to an input stack map frame.
+ */
+ static final int BASE = 0x1000000;
+
+ /**
+ * Base kind of the base reference types. The BASE_VALUE of such types is an
+ * index into the type table.
+ */
+ static final int OBJECT = BASE | 0x700000;
+
+ /**
+ * Base kind of the uninitialized base types. The BASE_VALUE of such types
+ * in an index into the type table (the Item at that index contains both an
+ * instruction offset and an internal class name).
+ */
+ static final int UNINITIALIZED = BASE | 0x800000;
+
+ /**
+ * Kind of the types that are relative to the local variable types of an
+ * input stack map frame. The value of such types is a local variable index.
+ */
+ private static final int LOCAL = 0x2000000;
+
+ /**
+ * Kind of the the types that are relative to the stack of an input stack
+ * map frame. The value of such types is a position relatively to the top of
+ * this stack.
+ */
+ private static final int STACK = 0x3000000;
+
+ /**
+ * The TOP type. This is a BASE type.
+ */
+ static final int TOP = BASE | 0;
+
+ /**
+ * The BOOLEAN type. This is a BASE type mainly used for array types.
+ */
+ static final int BOOLEAN = BASE | 9;
+
+ /**
+ * The BYTE type. This is a BASE type mainly used for array types.
+ */
+ static final int BYTE = BASE | 10;
+
+ /**
+ * The CHAR type. This is a BASE type mainly used for array types.
+ */
+ static final int CHAR = BASE | 11;
+
+ /**
+ * The SHORT type. This is a BASE type mainly used for array types.
+ */
+ static final int SHORT = BASE | 12;
+
+ /**
+ * The INTEGER type. This is a BASE type.
+ */
+ static final int INTEGER = BASE | 1;
+
+ /**
+ * The FLOAT type. This is a BASE type.
+ */
+ static final int FLOAT = BASE | 2;
+
+ /**
+ * The DOUBLE type. This is a BASE type.
+ */
+ static final int DOUBLE = BASE | 3;
+
+ /**
+ * The LONG type. This is a BASE type.
+ */
+ static final int LONG = BASE | 4;
+
+ /**
+ * The NULL type. This is a BASE type.
+ */
+ static final int NULL = BASE | 5;
+
+ /**
+ * The UNINITIALIZED_THIS type. This is a BASE type.
+ */
+ static final int UNINITIALIZED_THIS = BASE | 6;
+
+ /**
+ * The stack size variation corresponding to each JVM instruction. This
+ * stack variation is equal to the size of the values produced by an
+ * instruction, minus the size of the values consumed by this instruction.
+ */
+ static final int[] SIZE;
+
+ /**
+ * Computes the stack size variation corresponding to each JVM instruction.
+ */
+ static {
+ int i;
+ int[] b = new int[202];
+ String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD"
+ + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD"
+ + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED"
+ + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE";
+ for (i = 0; i < b.length; ++i) {
+ b[i] = s.charAt(i) - 'E';
+ }
+ SIZE = b;
+
+ // code to generate the above string
+ //
+ // int NA = 0; // not applicable (unused opcode or variable size opcode)
+ //
+ // b = new int[] {
+ // 0, //NOP, // visitInsn
+ // 1, //ACONST_NULL, // -
+ // 1, //ICONST_M1, // -
+ // 1, //ICONST_0, // -
+ // 1, //ICONST_1, // -
+ // 1, //ICONST_2, // -
+ // 1, //ICONST_3, // -
+ // 1, //ICONST_4, // -
+ // 1, //ICONST_5, // -
+ // 2, //LCONST_0, // -
+ // 2, //LCONST_1, // -
+ // 1, //FCONST_0, // -
+ // 1, //FCONST_1, // -
+ // 1, //FCONST_2, // -
+ // 2, //DCONST_0, // -
+ // 2, //DCONST_1, // -
+ // 1, //BIPUSH, // visitIntInsn
+ // 1, //SIPUSH, // -
+ // 1, //LDC, // visitLdcInsn
+ // NA, //LDC_W, // -
+ // NA, //LDC2_W, // -
+ // 1, //ILOAD, // visitVarInsn
+ // 2, //LLOAD, // -
+ // 1, //FLOAD, // -
+ // 2, //DLOAD, // -
+ // 1, //ALOAD, // -
+ // NA, //ILOAD_0, // -
+ // NA, //ILOAD_1, // -
+ // NA, //ILOAD_2, // -
+ // NA, //ILOAD_3, // -
+ // NA, //LLOAD_0, // -
+ // NA, //LLOAD_1, // -
+ // NA, //LLOAD_2, // -
+ // NA, //LLOAD_3, // -
+ // NA, //FLOAD_0, // -
+ // NA, //FLOAD_1, // -
+ // NA, //FLOAD_2, // -
+ // NA, //FLOAD_3, // -
+ // NA, //DLOAD_0, // -
+ // NA, //DLOAD_1, // -
+ // NA, //DLOAD_2, // -
+ // NA, //DLOAD_3, // -
+ // NA, //ALOAD_0, // -
+ // NA, //ALOAD_1, // -
+ // NA, //ALOAD_2, // -
+ // NA, //ALOAD_3, // -
+ // -1, //IALOAD, // visitInsn
+ // 0, //LALOAD, // -
+ // -1, //FALOAD, // -
+ // 0, //DALOAD, // -
+ // -1, //AALOAD, // -
+ // -1, //BALOAD, // -
+ // -1, //CALOAD, // -
+ // -1, //SALOAD, // -
+ // -1, //ISTORE, // visitVarInsn
+ // -2, //LSTORE, // -
+ // -1, //FSTORE, // -
+ // -2, //DSTORE, // -
+ // -1, //ASTORE, // -
+ // NA, //ISTORE_0, // -
+ // NA, //ISTORE_1, // -
+ // NA, //ISTORE_2, // -
+ // NA, //ISTORE_3, // -
+ // NA, //LSTORE_0, // -
+ // NA, //LSTORE_1, // -
+ // NA, //LSTORE_2, // -
+ // NA, //LSTORE_3, // -
+ // NA, //FSTORE_0, // -
+ // NA, //FSTORE_1, // -
+ // NA, //FSTORE_2, // -
+ // NA, //FSTORE_3, // -
+ // NA, //DSTORE_0, // -
+ // NA, //DSTORE_1, // -
+ // NA, //DSTORE_2, // -
+ // NA, //DSTORE_3, // -
+ // NA, //ASTORE_0, // -
+ // NA, //ASTORE_1, // -
+ // NA, //ASTORE_2, // -
+ // NA, //ASTORE_3, // -
+ // -3, //IASTORE, // visitInsn
+ // -4, //LASTORE, // -
+ // -3, //FASTORE, // -
+ // -4, //DASTORE, // -
+ // -3, //AASTORE, // -
+ // -3, //BASTORE, // -
+ // -3, //CASTORE, // -
+ // -3, //SASTORE, // -
+ // -1, //POP, // -
+ // -2, //POP2, // -
+ // 1, //DUP, // -
+ // 1, //DUP_X1, // -
+ // 1, //DUP_X2, // -
+ // 2, //DUP2, // -
+ // 2, //DUP2_X1, // -
+ // 2, //DUP2_X2, // -
+ // 0, //SWAP, // -
+ // -1, //IADD, // -
+ // -2, //LADD, // -
+ // -1, //FADD, // -
+ // -2, //DADD, // -
+ // -1, //ISUB, // -
+ // -2, //LSUB, // -
+ // -1, //FSUB, // -
+ // -2, //DSUB, // -
+ // -1, //IMUL, // -
+ // -2, //LMUL, // -
+ // -1, //FMUL, // -
+ // -2, //DMUL, // -
+ // -1, //IDIV, // -
+ // -2, //LDIV, // -
+ // -1, //FDIV, // -
+ // -2, //DDIV, // -
+ // -1, //IREM, // -
+ // -2, //LREM, // -
+ // -1, //FREM, // -
+ // -2, //DREM, // -
+ // 0, //INEG, // -
+ // 0, //LNEG, // -
+ // 0, //FNEG, // -
+ // 0, //DNEG, // -
+ // -1, //ISHL, // -
+ // -1, //LSHL, // -
+ // -1, //ISHR, // -
+ // -1, //LSHR, // -
+ // -1, //IUSHR, // -
+ // -1, //LUSHR, // -
+ // -1, //IAND, // -
+ // -2, //LAND, // -
+ // -1, //IOR, // -
+ // -2, //LOR, // -
+ // -1, //IXOR, // -
+ // -2, //LXOR, // -
+ // 0, //IINC, // visitIincInsn
+ // 1, //I2L, // visitInsn
+ // 0, //I2F, // -
+ // 1, //I2D, // -
+ // -1, //L2I, // -
+ // -1, //L2F, // -
+ // 0, //L2D, // -
+ // 0, //F2I, // -
+ // 1, //F2L, // -
+ // 1, //F2D, // -
+ // -1, //D2I, // -
+ // 0, //D2L, // -
+ // -1, //D2F, // -
+ // 0, //I2B, // -
+ // 0, //I2C, // -
+ // 0, //I2S, // -
+ // -3, //LCMP, // -
+ // -1, //FCMPL, // -
+ // -1, //FCMPG, // -
+ // -3, //DCMPL, // -
+ // -3, //DCMPG, // -
+ // -1, //IFEQ, // visitJumpInsn
+ // -1, //IFNE, // -
+ // -1, //IFLT, // -
+ // -1, //IFGE, // -
+ // -1, //IFGT, // -
+ // -1, //IFLE, // -
+ // -2, //IF_ICMPEQ, // -
+ // -2, //IF_ICMPNE, // -
+ // -2, //IF_ICMPLT, // -
+ // -2, //IF_ICMPGE, // -
+ // -2, //IF_ICMPGT, // -
+ // -2, //IF_ICMPLE, // -
+ // -2, //IF_ACMPEQ, // -
+ // -2, //IF_ACMPNE, // -
+ // 0, //GOTO, // -
+ // 1, //JSR, // -
+ // 0, //RET, // visitVarInsn
+ // -1, //TABLESWITCH, // visiTableSwitchInsn
+ // -1, //LOOKUPSWITCH, // visitLookupSwitch
+ // -1, //IRETURN, // visitInsn
+ // -2, //LRETURN, // -
+ // -1, //FRETURN, // -
+ // -2, //DRETURN, // -
+ // -1, //ARETURN, // -
+ // 0, //RETURN, // -
+ // NA, //GETSTATIC, // visitFieldInsn
+ // NA, //PUTSTATIC, // -
+ // NA, //GETFIELD, // -
+ // NA, //PUTFIELD, // -
+ // NA, //INVOKEVIRTUAL, // visitMethodInsn
+ // NA, //INVOKESPECIAL, // -
+ // NA, //INVOKESTATIC, // -
+ // NA, //INVOKEINTERFACE, // -
+ // NA, //UNUSED, // NOT VISITED
+ // 1, //NEW, // visitTypeInsn
+ // 0, //NEWARRAY, // visitIntInsn
+ // 0, //ANEWARRAY, // visitTypeInsn
+ // 0, //ARRAYLENGTH, // visitInsn
+ // NA, //ATHROW, // -
+ // 0, //CHECKCAST, // visitTypeInsn
+ // 0, //INSTANCEOF, // -
+ // -1, //MONITORENTER, // visitInsn
+ // -1, //MONITOREXIT, // -
+ // NA, //WIDE, // NOT VISITED
+ // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn
+ // -1, //IFNULL, // visitJumpInsn
+ // -1, //IFNONNULL, // -
+ // NA, //GOTO_W, // -
+ // NA, //JSR_W, // -
+ // };
+ // for (i = 0; i < b.length; ++i) {
+ // System.err.print((char)('E' + b[i]));
+ // }
+ // System.err.println();
+ }
+
+ /**
+ * The label (i.e. basic block) to which these input and output stack map
+ * frames correspond.
+ */
+ Label owner;
+
+ /**
+ * The input stack map frame locals.
+ */
+ int[] inputLocals;
+
+ /**
+ * The input stack map frame stack.
+ */
+ int[] inputStack;
+
+ /**
+ * The output stack map frame locals.
+ */
+ private int[] outputLocals;
+
+ /**
+ * The output stack map frame stack.
+ */
+ private int[] outputStack;
+
+ /**
+ * Relative size of the output stack. The exact semantics of this field
+ * depends on the algorithm that is used.
+ *
+ * When only the maximum stack size is computed, this field is the size of
+ * the output stack relatively to the top of the input stack.
+ *
+ * When the stack map frames are completely computed, this field is the
+ * actual number of types in {@link #outputStack}.
+ */
+ private int outputStackTop;
+
+ /**
+ * Number of types that are initialized in the basic block.
+ *
+ * @see #initializations
+ */
+ private int initializationCount;
+
+ /**
+ * The types that are initialized in the basic block. A constructor
+ * invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace
+ * <i>every occurence</i> of this type in the local variables and in the
+ * operand stack. This cannot be done during the first phase of the
+ * algorithm since, during this phase, the local variables and the operand
+ * stack are not completely computed. It is therefore necessary to store the
+ * types on which constructors are invoked in the basic block, in order to
+ * do this replacement during the second phase of the algorithm, where the
+ * frames are fully computed. Note that this array can contain types that
+ * are relative to input locals or to the input stack (see below for the
+ * description of the algorithm).
+ */
+ private int[] initializations;
+
+ /**
+ * Returns the output frame local variable type at the given index.
+ *
+ * @param local the index of the local that must be returned.
+ * @return the output frame local variable type at the given index.
+ */
+ private int get(final int local) {
+ if (outputLocals == null || local >= outputLocals.length) {
+ // this local has never been assigned in this basic block,
+ // so it is still equal to its value in the input frame
+ return LOCAL | local;
+ } else {
+ int type = outputLocals[local];
+ if (type == 0) {
+ // this local has never been assigned in this basic block,
+ // so it is still equal to its value in the input frame
+ type = outputLocals[local] = LOCAL | local;
+ }
+ return type;
+ }
+ }
+
+ /**
+ * Sets the output frame local variable type at the given index.
+ *
+ * @param local the index of the local that must be set.
+ * @param type the value of the local that must be set.
+ */
+ private void set(final int local, final int type) {
+ // creates and/or resizes the output local variables array if necessary
+ if (outputLocals == null) {
+ outputLocals = new int[10];
+ }
+ int n = outputLocals.length;
+ if (local >= n) {
+ int[] t = new int[Math.max(local + 1, 2 * n)];
+ System.arraycopy(outputLocals, 0, t, 0, n);
+ outputLocals = t;
+ }
+ // sets the local variable
+ outputLocals[local] = type;
+ }
+
+ /**
+ * Pushes a new type onto the output frame stack.
+ *
+ * @param type the type that must be pushed.
+ */
+ private void push(final int type) {
+ // creates and/or resizes the output stack array if necessary
+ if (outputStack == null) {
+ outputStack = new int[10];
+ }
+ int n = outputStack.length;
+ if (outputStackTop >= n) {
+ int[] t = new int[Math.max(outputStackTop + 1, 2 * n)];
+ System.arraycopy(outputStack, 0, t, 0, n);
+ outputStack = t;
+ }
+ // pushes the type on the output stack
+ outputStack[outputStackTop++] = type;
+ // updates the maximun height reached by the output stack, if needed
+ int top = owner.inputStackTop + outputStackTop;
+ if (top > owner.outputStackMax) {
+ owner.outputStackMax = top;
+ }
+ }
+
+ /**
+ * Pushes a new type onto the output frame stack.
+ *
+ * @param cw the ClassWriter to which this label belongs.
+ * @param desc the descriptor of the type to be pushed. Can also be a method
+ * descriptor (in this case this method pushes its return type onto
+ * the output frame stack).
+ */
+ private void push(final ClassWriter cw, final String desc) {
+ int type = type(cw, desc);
+ if (type != 0) {
+ push(type);
+ if (type == LONG || type == DOUBLE) {
+ push(TOP);
+ }
+ }
+ }
+
+ /**
+ * Returns the int encoding of the given type.
+ *
+ * @param cw the ClassWriter to which this label belongs.
+ * @param desc a type descriptor.
+ * @return the int encoding of the given type.
+ */
+ private static int type(final ClassWriter cw, final String desc) {
+ String t;
+ int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0;
+ switch (desc.charAt(index)) {
+ case 'V':
+ return 0;
+ case 'Z':
+ case 'C':
+ case 'B':
+ case 'S':
+ case 'I':
+ return INTEGER;
+ case 'F':
+ return FLOAT;
+ case 'J':
+ return LONG;
+ case 'D':
+ return DOUBLE;
+ case 'L':
+ // stores the internal name, not the descriptor!
+ t = desc.substring(index + 1, desc.length() - 1);
+ return OBJECT | cw.addType(t);
+ // case '[':
+ default:
+ // extracts the dimensions and the element type
+ int data;
+ int dims = index + 1;
+ while (desc.charAt(dims) == '[') {
+ ++dims;
+ }
+ switch (desc.charAt(dims)) {
+ case 'Z':
+ data = BOOLEAN;
+ break;
+ case 'C':
+ data = CHAR;
+ break;
+ case 'B':
+ data = BYTE;
+ break;
+ case 'S':
+ data = SHORT;
+ break;
+ case 'I':
+ data = INTEGER;
+ break;
+ case 'F':
+ data = FLOAT;
+ break;
+ case 'J':
+ data = LONG;
+ break;
+ case 'D':
+ data = DOUBLE;
+ break;
+ // case 'L':
+ default:
+ // stores the internal name, not the descriptor
+ t = desc.substring(dims + 1, desc.length() - 1);
+ data = OBJECT | cw.addType(t);
+ }
+ return (dims - index) << 28 | data;
+ }
+ }
+
+ /**
+ * Pops a type from the output frame stack and returns its value.
+ *
+ * @return the type that has been popped from the output frame stack.
+ */
+ private int pop() {
+ if (outputStackTop > 0) {
+ return outputStack[--outputStackTop];
+ } else {
+ // if the output frame stack is empty, pops from the input stack
+ return STACK | -(--owner.inputStackTop);
+ }
+ }
+
+ /**
+ * Pops the given number of types from the output frame stack.
+ *
+ * @param elements the number of types that must be popped.
+ */
+ private void pop(final int elements) {
+ if (outputStackTop >= elements) {
+ outputStackTop -= elements;
+ } else {
+ // if the number of elements to be popped is greater than the number
+ // of elements in the output stack, clear it, and pops the remaining
+ // elements from the input stack.
+ owner.inputStackTop -= elements - outputStackTop;
+ outputStackTop = 0;
+ }
+ }
+
+ /**
+ * Pops a type from the output frame stack.
+ *
+ * @param desc the descriptor of the type to be popped. Can also be a method
+ * descriptor (in this case this method pops the types corresponding
+ * to the method arguments).
+ */
+ private void pop(final String desc) {
+ char c = desc.charAt(0);
+ if (c == '(') {
+ pop((MethodWriter.getArgumentsAndReturnSizes(desc) >> 2) - 1);
+ } else if (c == 'J' || c == 'D') {
+ pop(2);
+ } else {
+ pop(1);
+ }
+ }
+
+ /**
+ * Adds a new type to the list of types on which a constructor is invoked in
+ * the basic block.
+ *
+ * @param var a type on a which a constructor is invoked.
+ */
+ private void init(final int var) {
+ // creates and/or resizes the initializations array if necessary
+ if (initializations == null) {
+ initializations = new int[2];
+ }
+ int n = initializations.length;
+ if (initializationCount >= n) {
+ int[] t = new int[Math.max(initializationCount + 1, 2 * n)];
+ System.arraycopy(initializations, 0, t, 0, n);
+ initializations = t;
+ }
+ // stores the type to be initialized
+ initializations[initializationCount++] = var;
+ }
+
+ /**
+ * Replaces the given type with the appropriate type if it is one of the
+ * types on which a constructor is invoked in the basic block.
+ *
+ * @param cw the ClassWriter to which this label belongs.
+ * @param t a type
+ * @return t or, if t is one of the types on which a constructor is invoked
+ * in the basic block, the type corresponding to this constructor.
+ */
+ private int init(final ClassWriter cw, final int t) {
+ int s;
+ if (t == UNINITIALIZED_THIS) {
+ s = OBJECT | cw.addType(cw.thisName);
+ } else if ((t & (DIM | BASE_KIND)) == UNINITIALIZED) {
+ String type = cw.typeTable[t & BASE_VALUE].strVal1;
+ s = OBJECT | cw.addType(type);
+ } else {
+ return t;
+ }
+ for (int j = 0; j < initializationCount; ++j) {
+ int u = initializations[j];
+ int dim = u & DIM;
+ int kind = u & KIND;
+ if (kind == LOCAL) {
+ u = dim + inputLocals[u & VALUE];
+ } else if (kind == STACK) {
+ u = dim + inputStack[inputStack.length - (u & VALUE)];
+ }
+ if (t == u) {
+ return s;
+ }
+ }
+ return t;
+ }
+
+ /**
+ * Initializes the input frame of the first basic block from the method
+ * descriptor.
+ *
+ * @param cw the ClassWriter to which this label belongs.
+ * @param access the access flags of the method to which this label belongs.
+ * @param args the formal parameter types of this method.
+ * @param maxLocals the maximum number of local variables of this method.
+ */
+ void initInputFrame(
+ final ClassWriter cw,
+ final int access,
+ final Type[] args,
+ final int maxLocals)
+ {
+ inputLocals = new int[maxLocals];
+ inputStack = new int[0];
+ int i = 0;
+ if ((access & Opcodes.ACC_STATIC) == 0) {
+ if ((access & MethodWriter.ACC_CONSTRUCTOR) == 0) {
+ inputLocals[i++] = OBJECT | cw.addType(cw.thisName);
+ } else {
+ inputLocals[i++] = UNINITIALIZED_THIS;
+ }
+ }
+ for (int j = 0; j < args.length; ++j) {
+ int t = type(cw, args[j].getDescriptor());
+ inputLocals[i++] = t;
+ if (t == LONG || t == DOUBLE) {
+ inputLocals[i++] = TOP;
+ }
+ }
+ while (i < maxLocals) {
+ inputLocals[i++] = TOP;
+ }
+ }
+
+ /**
+ * Simulates the action of the given instruction on the output stack frame.
+ *
+ * @param opcode the opcode of the instruction.
+ * @param arg the operand of the instruction, if any.
+ * @param cw the class writer to which this label belongs.
+ * @param item the operand of the instructions, if any.
+ */
+ void execute(
+ final int opcode,
+ final int arg,
+ final ClassWriter cw,
+ final Item item)
+ {
+ int t1, t2, t3, t4;
+ switch (opcode) {
+ case Opcodes.NOP:
+ case Opcodes.INEG:
+ case Opcodes.LNEG:
+ case Opcodes.FNEG:
+ case Opcodes.DNEG:
+ case Opcodes.I2B:
+ case Opcodes.I2C:
+ case Opcodes.I2S:
+ case Opcodes.GOTO:
+ case Opcodes.RETURN:
+ break;
+ case Opcodes.ACONST_NULL:
+ push(NULL);
+ break;
+ case Opcodes.ICONST_M1:
+ case Opcodes.ICONST_0:
+ case Opcodes.ICONST_1:
+ case Opcodes.ICONST_2:
+ case Opcodes.ICONST_3:
+ case Opcodes.ICONST_4:
+ case Opcodes.ICONST_5:
+ case Opcodes.BIPUSH:
+ case Opcodes.SIPUSH:
+ case Opcodes.ILOAD:
+ push(INTEGER);
+ break;
+ case Opcodes.LCONST_0:
+ case Opcodes.LCONST_1:
+ case Opcodes.LLOAD:
+ push(LONG);
+ push(TOP);
+ break;
+ case Opcodes.FCONST_0:
+ case Opcodes.FCONST_1:
+ case Opcodes.FCONST_2:
+ case Opcodes.FLOAD:
+ push(FLOAT);
+ break;
+ case Opcodes.DCONST_0:
+ case Opcodes.DCONST_1:
+ case Opcodes.DLOAD:
+ push(DOUBLE);
+ push(TOP);
+ break;
+ case Opcodes.LDC:
+ switch (item.type) {
+ case ClassWriter.INT:
+ push(INTEGER);
+ break;
+ case ClassWriter.LONG:
+ push(LONG);
+ push(TOP);
+ break;
+ case ClassWriter.FLOAT:
+ push(FLOAT);
+ break;
+ case ClassWriter.DOUBLE:
+ push(DOUBLE);
+ push(TOP);
+ break;
+ case ClassWriter.CLASS:
+ push(OBJECT | cw.addType("java/lang/Class"));
+ break;
+ // case ClassWriter.STR:
+ default:
+ push(OBJECT | cw.addType("java/lang/String"));
+ }
+ break;
+ case Opcodes.ALOAD:
+ push(get(arg));
+ break;
+ case Opcodes.IALOAD:
+ case Opcodes.BALOAD:
+ case Opcodes.CALOAD:
+ case Opcodes.SALOAD:
+ pop(2);
+ push(INTEGER);
+ break;
+ case Opcodes.LALOAD:
+ case Opcodes.D2L:
+ pop(2);
+ push(LONG);
+ push(TOP);
+ break;
+ case Opcodes.FALOAD:
+ pop(2);
+ push(FLOAT);
+ break;
+ case Opcodes.DALOAD:
+ case Opcodes.L2D:
+ pop(2);
+ push(DOUBLE);
+ push(TOP);
+ break;
+ case Opcodes.AALOAD:
+ pop(1);
+ t1 = pop();
+ push(ELEMENT_OF + t1);
+ break;
+ case Opcodes.ISTORE:
+ case Opcodes.FSTORE:
+ case Opcodes.ASTORE:
+ t1 = pop();
+ set(arg, t1);
+ if (arg > 0) {
+ t2 = get(arg - 1);
+ // if t2 is of kind STACK or LOCAL we cannot know its size!
+ if (t2 == LONG || t2 == DOUBLE) {
+ set(arg - 1, TOP);
+ }
+ }
+ break;
+ case Opcodes.LSTORE:
+ case Opcodes.DSTORE:
+ pop(1);
+ t1 = pop();
+ set(arg, t1);
+ set(arg + 1, TOP);
+ if (arg > 0) {
+ t2 = get(arg - 1);
+ // if t2 is of kind STACK or LOCAL we cannot know its size!
+ if (t2 == LONG || t2 == DOUBLE) {
+ set(arg - 1, TOP);
+ }
+ }
+ break;
+ case Opcodes.IASTORE:
+ case Opcodes.BASTORE:
+ case Opcodes.CASTORE:
+ case Opcodes.SASTORE:
+ case Opcodes.FASTORE:
+ case Opcodes.AASTORE:
+ pop(3);
+ break;
+ case Opcodes.LASTORE:
+ case Opcodes.DASTORE:
+ pop(4);
+ break;
+ case Opcodes.POP:
+ case Opcodes.IFEQ:
+ case Opcodes.IFNE:
+ case Opcodes.IFLT:
+ case Opcodes.IFGE:
+ case Opcodes.IFGT:
+ case Opcodes.IFLE:
+ case Opcodes.IRETURN:
+ case Opcodes.FRETURN:
+ case Opcodes.ARETURN:
+ case Opcodes.TABLESWITCH:
+ case Opcodes.LOOKUPSWITCH:
+ case Opcodes.ATHROW:
+ case Opcodes.MONITORENTER:
+ case Opcodes.MONITOREXIT:
+ case Opcodes.IFNULL:
+ case Opcodes.IFNONNULL:
+ pop(1);
+ break;
+ case Opcodes.POP2:
+ case Opcodes.IF_ICMPEQ:
+ case Opcodes.IF_ICMPNE:
+ case Opcodes.IF_ICMPLT:
+ case Opcodes.IF_ICMPGE:
+ case Opcodes.IF_ICMPGT:
+ case Opcodes.IF_ICMPLE:
+ case Opcodes.IF_ACMPEQ:
+ case Opcodes.IF_ACMPNE:
+ case Opcodes.LRETURN:
+ case Opcodes.DRETURN:
+ pop(2);
+ break;
+ case Opcodes.DUP:
+ t1 = pop();
+ push(t1);
+ push(t1);
+ break;
+ case Opcodes.DUP_X1:
+ t1 = pop();
+ t2 = pop();
+ push(t1);
+ push(t2);
+ push(t1);
+ break;
+ case Opcodes.DUP_X2:
+ t1 = pop();
+ t2 = pop();
+ t3 = pop();
+ push(t1);
+ push(t3);
+ push(t2);
+ push(t1);
+ break;
+ case Opcodes.DUP2:
+ t1 = pop();
+ t2 = pop();
+ push(t2);
+ push(t1);
+ push(t2);
+ push(t1);
+ break;
+ case Opcodes.DUP2_X1:
+ t1 = pop();
+ t2 = pop();
+ t3 = pop();
+ push(t2);
+ push(t1);
+ push(t3);
+ push(t2);
+ push(t1);
+ break;
+ case Opcodes.DUP2_X2:
+ t1 = pop();
+ t2 = pop();
+ t3 = pop();
+ t4 = pop();
+ push(t2);
+ push(t1);
+ push(t4);
+ push(t3);
+ push(t2);
+ push(t1);
+ break;
+ case Opcodes.SWAP:
+ t1 = pop();
+ t2 = pop();
+ push(t1);
+ push(t2);
+ break;
+ case Opcodes.IADD:
+ case Opcodes.ISUB:
+ case Opcodes.IMUL:
+ case Opcodes.IDIV:
+ case Opcodes.IREM:
+ case Opcodes.IAND:
+ case Opcodes.IOR:
+ case Opcodes.IXOR:
+ case Opcodes.ISHL:
+ case Opcodes.ISHR:
+ case Opcodes.IUSHR:
+ case Opcodes.L2I:
+ case Opcodes.D2I:
+ case Opcodes.FCMPL:
+ case Opcodes.FCMPG:
+ pop(2);
+ push(INTEGER);
+ break;
+ case Opcodes.LADD:
+ case Opcodes.LSUB:
+ case Opcodes.LMUL:
+ case Opcodes.LDIV:
+ case Opcodes.LREM:
+ case Opcodes.LAND:
+ case Opcodes.LOR:
+ case Opcodes.LXOR:
+ pop(4);
+ push(LONG);
+ push(TOP);
+ break;
+ case Opcodes.FADD:
+ case Opcodes.FSUB:
+ case Opcodes.FMUL:
+ case Opcodes.FDIV:
+ case Opcodes.FREM:
+ case Opcodes.L2F:
+ case Opcodes.D2F:
+ pop(2);
+ push(FLOAT);
+ break;
+ case Opcodes.DADD:
+ case Opcodes.DSUB:
+ case Opcodes.DMUL:
+ case Opcodes.DDIV:
+ case Opcodes.DREM:
+ pop(4);
+ push(DOUBLE);
+ push(TOP);
+ break;
+ case Opcodes.LSHL:
+ case Opcodes.LSHR:
+ case Opcodes.LUSHR:
+ pop(3);
+ push(LONG);
+ push(TOP);
+ break;
+ case Opcodes.IINC:
+ set(arg, INTEGER);
+ break;
+ case Opcodes.I2L:
+ case Opcodes.F2L:
+ pop(1);
+ push(LONG);
+ push(TOP);
+ break;
+ case Opcodes.I2F:
+ pop(1);
+ push(FLOAT);
+ break;
+ case Opcodes.I2D:
+ case Opcodes.F2D:
+ pop(1);
+ push(DOUBLE);
+ push(TOP);
+ break;
+ case Opcodes.F2I:
+ case Opcodes.ARRAYLENGTH:
+ case Opcodes.INSTANCEOF:
+ pop(1);
+ push(INTEGER);
+ break;
+ case Opcodes.LCMP:
+ case Opcodes.DCMPL:
+ case Opcodes.DCMPG:
+ pop(4);
+ push(INTEGER);
+ break;
+ case Opcodes.JSR:
+ case Opcodes.RET:
+ throw new RuntimeException("JSR/RET are not supported with computeFrames option");
+ case Opcodes.GETSTATIC:
+ push(cw, item.strVal3);
+ break;
+ case Opcodes.PUTSTATIC:
+ pop(item.strVal3);
+ break;
+ case Opcodes.GETFIELD:
+ pop(1);
+ push(cw, item.strVal3);
+ break;
+ case Opcodes.PUTFIELD:
+ pop(item.strVal3);
+ pop();
+ break;
+ case Opcodes.INVOKEVIRTUAL:
+ case Opcodes.INVOKESPECIAL:
+ case Opcodes.INVOKESTATIC:
+ case Opcodes.INVOKEINTERFACE:
+ pop(item.strVal3);
+ if (opcode != Opcodes.INVOKESTATIC) {
+ t1 = pop();
+ if (opcode == Opcodes.INVOKESPECIAL
+ && item.strVal2.charAt(0) == '<')
+ {
+ init(t1);
+ }
+ }
+ push(cw, item.strVal3);
+ break;
+ case Opcodes.NEW:
+ push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, arg));
+ break;
+ case Opcodes.NEWARRAY:
+ pop();
+ switch (arg) {
+ case Opcodes.T_BOOLEAN:
+ push(ARRAY_OF | BOOLEAN);
+ break;
+ case Opcodes.T_CHAR:
+ push(ARRAY_OF | CHAR);
+ break;
+ case Opcodes.T_BYTE:
+ push(ARRAY_OF | BYTE);
+ break;
+ case Opcodes.T_SHORT:
+ push(ARRAY_OF | SHORT);
+ break;
+ case Opcodes.T_INT:
+ push(ARRAY_OF | INTEGER);
+ break;
+ case Opcodes.T_FLOAT:
+ push(ARRAY_OF | FLOAT);
+ break;
+ case Opcodes.T_DOUBLE:
+ push(ARRAY_OF | DOUBLE);
+ break;
+ // case Opcodes.T_LONG:
+ default:
+ push(ARRAY_OF | LONG);
+ break;
+ }
+ break;
+ case Opcodes.ANEWARRAY:
+ String s = item.strVal1;
+ pop();
+ if (s.charAt(0) == '[') {
+ push(cw, '[' + s);
+ } else {
+ push(ARRAY_OF | OBJECT | cw.addType(s));
+ }
+ break;
+ case Opcodes.CHECKCAST:
+ s = item.strVal1;
+ pop();
+ if (s.charAt(0) == '[') {
+ push(cw, s);
+ } else {
+ push(OBJECT | cw.addType(s));
+ }
+ break;
+ // case Opcodes.MULTIANEWARRAY:
+ default:
+ pop(arg);
+ push(cw, item.strVal1);
+ break;
+ }
+ }
+
+ /**
+ * Merges the input frame of the given basic block with the input and output
+ * frames of this basic block. Returns <tt>true</tt> if the input frame of
+ * the given label has been changed by this operation.
+ *
+ * @param cw the ClassWriter to which this label belongs.
+ * @param frame the basic block whose input frame must be updated.
+ * @param edge the kind of the {@link Edge} between this label and 'label'.
+ * See {@link Edge#info}.
+ * @return <tt>true</tt> if the input frame of the given label has been
+ * changed by this operation.
+ */
+ boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
+ boolean changed = false;
+ int i, s, dim, kind, t;
+
+ int nLocal = inputLocals.length;
+ int nStack = inputStack.length;
+ if (frame.inputLocals == null) {
+ frame.inputLocals = new int[nLocal];
+ changed = true;
+ }
+
+ for (i = 0; i < nLocal; ++i) {
+ if (outputLocals != null && i < outputLocals.length) {
+ s = outputLocals[i];
+ if (s == 0) {
+ t = inputLocals[i];
+ } else {
+ dim = s & DIM;
+ kind = s & KIND;
+ if (kind == LOCAL) {
+ t = dim + inputLocals[s & VALUE];
+ } else if (kind == STACK) {
+ t = dim + inputStack[nStack - (s & VALUE)];
+ } else {
+ t = s;
+ }
+ }
+ } else {
+ t = inputLocals[i];
+ }
+ if (initializations != null) {
+ t = init(cw, t);
+ }
+ changed |= merge(cw, t, frame.inputLocals, i);
+ }
+
+ if (edge > 0) {
+ for (i = 0; i < nLocal; ++i) {
+ t = inputLocals[i];
+ changed |= merge(cw, t, frame.inputLocals, i);
+ }
+ if (frame.inputStack == null) {
+ frame.inputStack = new int[1];
+ changed = true;
+ }
+ changed |= merge(cw, edge, frame.inputStack, 0);
+ return changed;
+ }
+
+ int nInputStack = inputStack.length + owner.inputStackTop;
+ if (frame.inputStack == null) {
+ frame.inputStack = new int[nInputStack + outputStackTop];
+ changed = true;
+ }
+
+ for (i = 0; i < nInputStack; ++i) {
+ t = inputStack[i];
+ if (initializations != null) {
+ t = init(cw, t);
+ }
+ changed |= merge(cw, t, frame.inputStack, i);
+ }
+ for (i = 0; i < outputStackTop; ++i) {
+ s = outputStack[i];
+ dim = s & DIM;
+ kind = s & KIND;
+ if (kind == LOCAL) {
+ t = dim + inputLocals[s & VALUE];
+ } else if (kind == STACK) {
+ t = dim + inputStack[nStack - (s & VALUE)];
+ } else {
+ t = s;
+ }
+ if (initializations != null) {
+ t = init(cw, t);
+ }
+ changed |= merge(cw, t, frame.inputStack, nInputStack + i);
+ }
+ return changed;
+ }
+
+ /**
+ * Merges the type at the given index in the given type array with the given
+ * type. Returns <tt>true</tt> if the type array has been modified by this
+ * operation.
+ *
+ * @param cw the ClassWriter to which this label belongs.
+ * @param t the type with which the type array element must be merged.
+ * @param types an array of types.
+ * @param index the index of the type that must be merged in 'types'.
+ * @return <tt>true</tt> if the type array has been modified by this
+ * operation.
+ */
+ private static boolean merge(
+ final ClassWriter cw,
+ int t,
+ final int[] types,
+ final int index)
+ {
+ int u = types[index];
+ if (u == t) {
+ // if the types are equal, merge(u,t)=u, so there is no change
+ return false;
+ }
+ if ((t & ~DIM) == NULL) {
+ if (u == NULL) {
+ return false;
+ }
+ t = NULL;
+ }
+ if (u == 0) {
+ // if types[index] has never been assigned, merge(u,t)=t
+ types[index] = t;
+ return true;
+ }
+ int v;
+ if ((u & BASE_KIND) == OBJECT || (u & DIM) != 0) {
+ // if u is a reference type of any dimension
+ if (t == NULL) {
+ // if t is the NULL type, merge(u,t)=u, so there is no change
+ return false;
+ } else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) {
+ if ((u & BASE_KIND) == OBJECT) {
+ // if t is also a reference type, and if u and t have the
+ // same dimension merge(u,t) = dim(t) | common parent of the
+ // element types of u and t
+ v = (t & DIM) | OBJECT
+ | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE);
+ } else {
+ // if u and t are array types, but not with the same element
+ // type, merge(u,t)=java/lang/Object
+ v = OBJECT | cw.addType("java/lang/Object");
+ }
+ } else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) {
+ // if t is any other reference or array type,
+ // merge(u,t)=java/lang/Object
+ v = OBJECT | cw.addType("java/lang/Object");
+ } else {
+ // if t is any other type, merge(u,t)=TOP
+ v = TOP;
+ }
+ } else if (u == NULL) {
+ // if u is the NULL type, merge(u,t)=t,
+ // or TOP if t is not a reference type
+ v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP;
+ } else {
+ // if u is any other type, merge(u,t)=TOP whatever t
+ v = TOP;
+ }
+ if (u != v) {
+ types[index] = v;
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/Handler.java b/cglib-and-asm/src/org/mockito/asm/Handler.java
new file mode 100644
index 0000000..09836cc
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/Handler.java
@@ -0,0 +1,70 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * Information about an exception handler block.
+ *
+ * @author Eric Bruneton
+ */
+class Handler {
+
+ /**
+ * Beginning of the exception handler's scope (inclusive).
+ */
+ Label start;
+
+ /**
+ * End of the exception handler's scope (exclusive).
+ */
+ Label end;
+
+ /**
+ * Beginning of the exception handler's code.
+ */
+ Label handler;
+
+ /**
+ * Internal name of the type of exceptions handled by this handler, or
+ * <tt>null</tt> to catch any exceptions.
+ */
+ String desc;
+
+ /**
+ * Constant pool index of the internal name of the type of exceptions
+ * handled by this handler, or 0 to catch any exceptions.
+ */
+ int type;
+
+ /**
+ * Next exception handler block info.
+ */
+ Handler next;
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/Item.java b/cglib-and-asm/src/org/mockito/asm/Item.java
new file mode 100644
index 0000000..90fdbfe
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/Item.java
@@ -0,0 +1,256 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * A constant pool item. Constant pool items can be created with the 'newXXX'
+ * methods in the {@link ClassWriter} class.
+ *
+ * @author Eric Bruneton
+ */
+final class Item {
+
+ /**
+ * Index of this item in the constant pool.
+ */
+ int index;
+
+ /**
+ * Type of this constant pool item. A single class is used to represent all
+ * constant pool item types, in order to minimize the bytecode size of this
+ * package. The value of this field is one of {@link ClassWriter#INT},
+ * {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT},
+ * {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8},
+ * {@link ClassWriter#STR}, {@link ClassWriter#CLASS},
+ * {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD},
+ * {@link ClassWriter#METH}, {@link ClassWriter#IMETH}.
+ *
+ * Special Item types are used for Items that are stored in the ClassWriter
+ * {@link ClassWriter#typeTable}, instead of the constant pool, in order to
+ * avoid clashes with normal constant pool items in the ClassWriter constant
+ * pool's hash table. These special item types are
+ * {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and
+ * {@link ClassWriter#TYPE_MERGED}.
+ */
+ int type;
+
+ /**
+ * Value of this item, for an integer item.
+ */
+ int intVal;
+
+ /**
+ * Value of this item, for a long item.
+ */
+ long longVal;
+
+ /**
+ * First part of the value of this item, for items that do not hold a
+ * primitive value.
+ */
+ String strVal1;
+
+ /**
+ * Second part of the value of this item, for items that do not hold a
+ * primitive value.
+ */
+ String strVal2;
+
+ /**
+ * Third part of the value of this item, for items that do not hold a
+ * primitive value.
+ */
+ String strVal3;
+
+ /**
+ * The hash code value of this constant pool item.
+ */
+ int hashCode;
+
+ /**
+ * Link to another constant pool item, used for collision lists in the
+ * constant pool's hash table.
+ */
+ Item next;
+
+ /**
+ * Constructs an uninitialized {@link Item}.
+ */
+ Item() {
+ }
+
+ /**
+ * Constructs an uninitialized {@link Item} for constant pool element at
+ * given position.
+ *
+ * @param index index of the item to be constructed.
+ */
+ Item(final int index) {
+ this.index = index;
+ }
+
+ /**
+ * Constructs a copy of the given item.
+ *
+ * @param index index of the item to be constructed.
+ * @param i the item that must be copied into the item to be constructed.
+ */
+ Item(final int index, final Item i) {
+ this.index = index;
+ type = i.type;
+ intVal = i.intVal;
+ longVal = i.longVal;
+ strVal1 = i.strVal1;
+ strVal2 = i.strVal2;
+ strVal3 = i.strVal3;
+ hashCode = i.hashCode;
+ }
+
+ /**
+ * Sets this item to an integer item.
+ *
+ * @param intVal the value of this item.
+ */
+ void set(final int intVal) {
+ this.type = ClassWriter.INT;
+ this.intVal = intVal;
+ this.hashCode = 0x7FFFFFFF & (type + intVal);
+ }
+
+ /**
+ * Sets this item to a long item.
+ *
+ * @param longVal the value of this item.
+ */
+ void set(final long longVal) {
+ this.type = ClassWriter.LONG;
+ this.longVal = longVal;
+ this.hashCode = 0x7FFFFFFF & (type + (int) longVal);
+ }
+
+ /**
+ * Sets this item to a float item.
+ *
+ * @param floatVal the value of this item.
+ */
+ void set(final float floatVal) {
+ this.type = ClassWriter.FLOAT;
+ this.intVal = Float.floatToRawIntBits(floatVal);
+ this.hashCode = 0x7FFFFFFF & (type + (int) floatVal);
+ }
+
+ /**
+ * Sets this item to a double item.
+ *
+ * @param doubleVal the value of this item.
+ */
+ void set(final double doubleVal) {
+ this.type = ClassWriter.DOUBLE;
+ this.longVal = Double.doubleToRawLongBits(doubleVal);
+ this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal);
+ }
+
+ /**
+ * Sets this item to an item that do not hold a primitive value.
+ *
+ * @param type the type of this item.
+ * @param strVal1 first part of the value of this item.
+ * @param strVal2 second part of the value of this item.
+ * @param strVal3 third part of the value of this item.
+ */
+ void set(
+ final int type,
+ final String strVal1,
+ final String strVal2,
+ final String strVal3)
+ {
+ this.type = type;
+ this.strVal1 = strVal1;
+ this.strVal2 = strVal2;
+ this.strVal3 = strVal3;
+ switch (type) {
+ case ClassWriter.UTF8:
+ case ClassWriter.STR:
+ case ClassWriter.CLASS:
+ case ClassWriter.TYPE_NORMAL:
+ hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
+ return;
+ case ClassWriter.NAME_TYPE:
+ hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
+ * strVal2.hashCode());
+ return;
+ // ClassWriter.FIELD:
+ // ClassWriter.METH:
+ // ClassWriter.IMETH:
+ default:
+ hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
+ * strVal2.hashCode() * strVal3.hashCode());
+ }
+ }
+
+ /**
+ * Indicates if the given item is equal to this one.
+ *
+ * @param i the item to be compared to this one.
+ * @return <tt>true</tt> if the given item if equal to this one,
+ * <tt>false</tt> otherwise.
+ */
+ boolean isEqualTo(final Item i) {
+ if (i.type == type) {
+ switch (type) {
+ case ClassWriter.INT:
+ case ClassWriter.FLOAT:
+ return i.intVal == intVal;
+ case ClassWriter.TYPE_MERGED:
+ case ClassWriter.LONG:
+ case ClassWriter.DOUBLE:
+ return i.longVal == longVal;
+ case ClassWriter.UTF8:
+ case ClassWriter.STR:
+ case ClassWriter.CLASS:
+ case ClassWriter.TYPE_NORMAL:
+ return i.strVal1.equals(strVal1);
+ case ClassWriter.TYPE_UNINIT:
+ return i.intVal == intVal && i.strVal1.equals(strVal1);
+ case ClassWriter.NAME_TYPE:
+ return i.strVal1.equals(strVal1)
+ && i.strVal2.equals(strVal2);
+ // ClassWriter.FIELD:
+ // ClassWriter.METH:
+ // ClassWriter.IMETH:
+ default:
+ return i.strVal1.equals(strVal1)
+ && i.strVal2.equals(strVal2)
+ && i.strVal3.equals(strVal3);
+ }
+ }
+ return false;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/Label.java b/cglib-and-asm/src/org/mockito/asm/Label.java
new file mode 100644
index 0000000..3394ca8
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/Label.java
@@ -0,0 +1,526 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * A label represents a position in the bytecode of a method. Labels are used
+ * for jump, goto, and switch instructions, and for try catch blocks.
+ *
+ * @author Eric Bruneton
+ */
+public class Label {
+
+ /**
+ * Indicates if this label is only used for debug attributes. Such a label
+ * is not the start of a basic block, the target of a jump instruction, or
+ * an exception handler. It can be safely ignored in control flow graph
+ * analysis algorithms (for optimization purposes).
+ */
+ static final int DEBUG = 1;
+
+ /**
+ * Indicates if the position of this label is known.
+ */
+ static final int RESOLVED = 2;
+
+ /**
+ * Indicates if this label has been updated, after instruction resizing.
+ */
+ static final int RESIZED = 4;
+
+ /**
+ * Indicates if this basic block has been pushed in the basic block stack.
+ * See {@link MethodWriter#visitMaxs visitMaxs}.
+ */
+ static final int PUSHED = 8;
+
+ /**
+ * Indicates if this label is the target of a jump instruction, or the start
+ * of an exception handler.
+ */
+ static final int TARGET = 16;
+
+ /**
+ * Indicates if a stack map frame must be stored for this label.
+ */
+ static final int STORE = 32;
+
+ /**
+ * Indicates if this label corresponds to a reachable basic block.
+ */
+ static final int REACHABLE = 64;
+
+ /**
+ * Indicates if this basic block ends with a JSR instruction.
+ */
+ static final int JSR = 128;
+
+ /**
+ * Indicates if this basic block ends with a RET instruction.
+ */
+ static final int RET = 256;
+
+ /**
+ * Indicates if this basic block is the start of a subroutine.
+ */
+ static final int SUBROUTINE = 512;
+
+ /**
+ * Indicates if this subroutine basic block has been visited.
+ */
+ static final int VISITED = 1024;
+
+ /**
+ * Field used to associate user information to a label. Warning: this field
+ * is used by the ASM tree package. In order to use it with the ASM tree
+ * package you must override the {@link
+ * org.mockito.asm.tree.MethodNode#getLabelNode} method.
+ */
+ public Object info;
+
+ /**
+ * Flags that indicate the status of this label.
+ *
+ * @see #DEBUG
+ * @see #RESOLVED
+ * @see #RESIZED
+ * @see #PUSHED
+ * @see #TARGET
+ * @see #STORE
+ * @see #REACHABLE
+ * @see #JSR
+ * @see #RET
+ */
+ int status;
+
+ /**
+ * The line number corresponding to this label, if known.
+ */
+ int line;
+
+ /**
+ * The position of this label in the code, if known.
+ */
+ int position;
+
+ /**
+ * Number of forward references to this label, times two.
+ */
+ private int referenceCount;
+
+ /**
+ * Informations about forward references. Each forward reference is
+ * described by two consecutive integers in this array: the first one is the
+ * position of the first byte of the bytecode instruction that contains the
+ * forward reference, while the second is the position of the first byte of
+ * the forward reference itself. In fact the sign of the first integer
+ * indicates if this reference uses 2 or 4 bytes, and its absolute value
+ * gives the position of the bytecode instruction. This array is also used
+ * as a bitset to store the subroutines to which a basic block belongs. This
+ * information is needed in {@linked MethodWriter#visitMaxs}, after all
+ * forward references have been resolved. Hence the same array can be used
+ * for both purposes without problems.
+ */
+ private int[] srcAndRefPositions;
+
+ // ------------------------------------------------------------------------
+
+ /*
+ * Fields for the control flow and data flow graph analysis algorithms (used
+ * to compute the maximum stack size or the stack map frames). A control
+ * flow graph contains one node per "basic block", and one edge per "jump"
+ * from one basic block to another. Each node (i.e., each basic block) is
+ * represented by the Label object that corresponds to the first instruction
+ * of this basic block. Each node also stores the list of its successors in
+ * the graph, as a linked list of Edge objects.
+ *
+ * The control flow analysis algorithms used to compute the maximum stack
+ * size or the stack map frames are similar and use two steps. The first
+ * step, during the visit of each instruction, builds information about the
+ * state of the local variables and the operand stack at the end of each
+ * basic block, called the "output frame", <i>relatively</i> to the frame
+ * state at the beginning of the basic block, which is called the "input
+ * frame", and which is <i>unknown</i> during this step. The second step,
+ * in {@link MethodWriter#visitMaxs}, is a fix point algorithm that
+ * computes information about the input frame of each basic block, from the
+ * input state of the first basic block (known from the method signature),
+ * and by the using the previously computed relative output frames.
+ *
+ * The algorithm used to compute the maximum stack size only computes the
+ * relative output and absolute input stack heights, while the algorithm
+ * used to compute stack map frames computes relative output frames and
+ * absolute input frames.
+ */
+
+ /**
+ * Start of the output stack relatively to the input stack. The exact
+ * semantics of this field depends on the algorithm that is used.
+ *
+ * When only the maximum stack size is computed, this field is the number of
+ * elements in the input stack.
+ *
+ * When the stack map frames are completely computed, this field is the
+ * offset of the first output stack element relatively to the top of the
+ * input stack. This offset is always negative or null. A null offset means
+ * that the output stack must be appended to the input stack. A -n offset
+ * means that the first n output stack elements must replace the top n input
+ * stack elements, and that the other elements must be appended to the input
+ * stack.
+ */
+ int inputStackTop;
+
+ /**
+ * Maximum height reached by the output stack, relatively to the top of the
+ * input stack. This maximum is always positive or null.
+ */
+ int outputStackMax;
+
+ /**
+ * Information about the input and output stack map frames of this basic
+ * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES}
+ * option is used.
+ */
+ Frame frame;
+
+ /**
+ * The successor of this label, in the order they are visited. This linked
+ * list does not include labels used for debug info only. If
+ * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it
+ * does not contain successive labels that denote the same bytecode position
+ * (in this case only the first label appears in this list).
+ */
+ Label successor;
+
+ /**
+ * The successors of this node in the control flow graph. These successors
+ * are stored in a linked list of {@link Edge Edge} objects, linked to each
+ * other by their {@link Edge#next} field.
+ */
+ Edge successors;
+
+ /**
+ * The next basic block in the basic block stack. This stack is used in the
+ * main loop of the fix point algorithm used in the second step of the
+ * control flow analysis algorithms.
+ *
+ * @see MethodWriter#visitMaxs
+ */
+ Label next;
+
+ // ------------------------------------------------------------------------
+ // Constructor
+ // ------------------------------------------------------------------------
+
+ /**
+ * Constructs a new label.
+ */
+ public Label() {
+ }
+
+ // ------------------------------------------------------------------------
+ // Methods to compute offsets and to manage forward references
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the offset corresponding to this label. This offset is computed
+ * from the start of the method's bytecode. <i>This method is intended for
+ * {@link Attribute} sub classes, and is normally not needed by class
+ * generators or adapters.</i>
+ *
+ * @return the offset corresponding to this label.
+ * @throws IllegalStateException if this label is not resolved yet.
+ */
+ public int getOffset() {
+ if ((status & RESOLVED) == 0) {
+ throw new IllegalStateException("Label offset position has not been resolved yet");
+ }
+ return position;
+ }
+
+ /**
+ * Puts a reference to this label in the bytecode of a method. If the
+ * position of the label is known, the offset is computed and written
+ * directly. Otherwise, a null offset is written and a new forward reference
+ * is declared for this label.
+ *
+ * @param owner the code writer that calls this method.
+ * @param out the bytecode of the method.
+ * @param source the position of first byte of the bytecode instruction that
+ * contains this label.
+ * @param wideOffset <tt>true</tt> if the reference must be stored in 4
+ * bytes, or <tt>false</tt> if it must be stored with 2 bytes.
+ * @throws IllegalArgumentException if this label has not been created by
+ * the given code writer.
+ */
+ void put(
+ final MethodWriter owner,
+ final ByteVector out,
+ final int source,
+ final boolean wideOffset)
+ {
+ if ((status & RESOLVED) == 0) {
+ if (wideOffset) {
+ addReference(-1 - source, out.length);
+ out.putInt(-1);
+ } else {
+ addReference(source, out.length);
+ out.putShort(-1);
+ }
+ } else {
+ if (wideOffset) {
+ out.putInt(position - source);
+ } else {
+ out.putShort(position - source);
+ }
+ }
+ }
+
+ /**
+ * Adds a forward reference to this label. This method must be called only
+ * for a true forward reference, i.e. only if this label is not resolved
+ * yet. For backward references, the offset of the reference can be, and
+ * must be, computed and stored directly.
+ *
+ * @param sourcePosition the position of the referencing instruction. This
+ * position will be used to compute the offset of this forward
+ * reference.
+ * @param referencePosition the position where the offset for this forward
+ * reference must be stored.
+ */
+ private void addReference(
+ final int sourcePosition,
+ final int referencePosition)
+ {
+ if (srcAndRefPositions == null) {
+ srcAndRefPositions = new int[6];
+ }
+ if (referenceCount >= srcAndRefPositions.length) {
+ int[] a = new int[srcAndRefPositions.length + 6];
+ System.arraycopy(srcAndRefPositions,
+ 0,
+ a,
+ 0,
+ srcAndRefPositions.length);
+ srcAndRefPositions = a;
+ }
+ srcAndRefPositions[referenceCount++] = sourcePosition;
+ srcAndRefPositions[referenceCount++] = referencePosition;
+ }
+
+ /**
+ * Resolves all forward references to this label. This method must be called
+ * when this label is added to the bytecode of the method, i.e. when its
+ * position becomes known. This method fills in the blanks that where left
+ * in the bytecode by each forward reference previously added to this label.
+ *
+ * @param owner the code writer that calls this method.
+ * @param position the position of this label in the bytecode.
+ * @param data the bytecode of the method.
+ * @return <tt>true</tt> if a blank that was left for this label was to
+ * small to store the offset. In such a case the corresponding jump
+ * instruction is replaced with a pseudo instruction (using unused
+ * opcodes) using an unsigned two bytes offset. These pseudo
+ * instructions will need to be replaced with true instructions with
+ * wider offsets (4 bytes instead of 2). This is done in
+ * {@link MethodWriter#resizeInstructions}.
+ * @throws IllegalArgumentException if this label has already been resolved,
+ * or if it has not been created by the given code writer.
+ */
+ boolean resolve(
+ final MethodWriter owner,
+ final int position,
+ final byte[] data)
+ {
+ boolean needUpdate = false;
+ this.status |= RESOLVED;
+ this.position = position;
+ int i = 0;
+ while (i < referenceCount) {
+ int source = srcAndRefPositions[i++];
+ int reference = srcAndRefPositions[i++];
+ int offset;
+ if (source >= 0) {
+ offset = position - source;
+ if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) {
+ /*
+ * changes the opcode of the jump instruction, in order to
+ * be able to find it later (see resizeInstructions in
+ * MethodWriter). These temporary opcodes are similar to
+ * jump instruction opcodes, except that the 2 bytes offset
+ * is unsigned (and can therefore represent values from 0 to
+ * 65535, which is sufficient since the size of a method is
+ * limited to 65535 bytes).
+ */
+ int opcode = data[reference - 1] & 0xFF;
+ if (opcode <= Opcodes.JSR) {
+ // changes IFEQ ... JSR to opcodes 202 to 217
+ data[reference - 1] = (byte) (opcode + 49);
+ } else {
+ // changes IFNULL and IFNONNULL to opcodes 218 and 219
+ data[reference - 1] = (byte) (opcode + 20);
+ }
+ needUpdate = true;
+ }
+ data[reference++] = (byte) (offset >>> 8);
+ data[reference] = (byte) offset;
+ } else {
+ offset = position + source + 1;
+ data[reference++] = (byte) (offset >>> 24);
+ data[reference++] = (byte) (offset >>> 16);
+ data[reference++] = (byte) (offset >>> 8);
+ data[reference] = (byte) offset;
+ }
+ }
+ return needUpdate;
+ }
+
+ /**
+ * Returns the first label of the series to which this label belongs. For an
+ * isolated label or for the first label in a series of successive labels,
+ * this method returns the label itself. For other labels it returns the
+ * first label of the series.
+ *
+ * @return the first label of the series to which this label belongs.
+ */
+ Label getFirst() {
+ return !ClassReader.FRAMES || frame == null ? this : frame.owner;
+ }
+
+ // ------------------------------------------------------------------------
+ // Methods related to subroutines
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns true is this basic block belongs to the given subroutine.
+ *
+ * @param id a subroutine id.
+ * @return true is this basic block belongs to the given subroutine.
+ */
+ boolean inSubroutine(final long id) {
+ if ((status & Label.VISITED) != 0) {
+ return (srcAndRefPositions[(int) (id >>> 32)] & (int) id) != 0;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this basic block and the given one belong to a common
+ * subroutine.
+ *
+ * @param block another basic block.
+ * @return true if this basic block and the given one belong to a common
+ * subroutine.
+ */
+ boolean inSameSubroutine(final Label block) {
+ for (int i = 0; i < srcAndRefPositions.length; ++i) {
+ if ((srcAndRefPositions[i] & block.srcAndRefPositions[i]) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Marks this basic block as belonging to the given subroutine.
+ *
+ * @param id a subroutine id.
+ * @param nbSubroutines the total number of subroutines in the method.
+ */
+ void addToSubroutine(final long id, final int nbSubroutines) {
+ if ((status & VISITED) == 0) {
+ status |= VISITED;
+ srcAndRefPositions = new int[(nbSubroutines - 1) / 32 + 1];
+ }
+ srcAndRefPositions[(int) (id >>> 32)] |= (int) id;
+ }
+
+ /**
+ * Finds the basic blocks that belong to a given subroutine, and marks these
+ * blocks as belonging to this subroutine. This recursive method follows the
+ * control flow graph to find all the blocks that are reachable from the
+ * current block WITHOUT following any JSR target.
+ *
+ * @param JSR a JSR block that jumps to this subroutine. If this JSR is not
+ * null it is added to the successor of the RET blocks found in the
+ * subroutine.
+ * @param id the id of this subroutine.
+ * @param nbSubroutines the total number of subroutines in the method.
+ */
+ void visitSubroutine(final Label JSR, final long id, final int nbSubroutines)
+ {
+ if (JSR != null) {
+ if ((status & VISITED) != 0) {
+ return;
+ }
+ status |= VISITED;
+ // adds JSR to the successors of this block, if it is a RET block
+ if ((status & RET) != 0) {
+ if (!inSameSubroutine(JSR)) {
+ Edge e = new Edge();
+ e.info = inputStackTop;
+ e.successor = JSR.successors.successor;
+ e.next = successors;
+ successors = e;
+ }
+ }
+ } else {
+ // if this block already belongs to subroutine 'id', returns
+ if (inSubroutine(id)) {
+ return;
+ }
+ // marks this block as belonging to subroutine 'id'
+ addToSubroutine(id, nbSubroutines);
+ }
+ // calls this method recursively on each successor, except JSR targets
+ Edge e = successors;
+ while (e != null) {
+ // if this block is a JSR block, then 'successors.next' leads
+ // to the JSR target (see {@link #visitJumpInsn}) and must therefore
+ // not be followed
+ if ((status & Label.JSR) == 0 || e != successors.next) {
+ e.successor.visitSubroutine(JSR, id, nbSubroutines);
+ }
+ e = e.next;
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Overriden Object methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns a string representation of this label.
+ *
+ * @return a string representation of this label.
+ */
+ public String toString() {
+ return "L" + System.identityHashCode(this);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/MethodAdapter.java b/cglib-and-asm/src/org/mockito/asm/MethodAdapter.java
new file mode 100644
index 0000000..56b3be8
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/MethodAdapter.java
@@ -0,0 +1,195 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * An empty {@link MethodVisitor} that delegates to another
+ * {@link MethodVisitor}. This class can be used as a super class to quickly
+ * implement usefull method adapter classes, just by overriding the necessary
+ * methods.
+ *
+ * @author Eric Bruneton
+ */
+public class MethodAdapter implements MethodVisitor {
+
+ /**
+ * The {@link MethodVisitor} to which this adapter delegates calls.
+ */
+ protected MethodVisitor mv;
+
+ /**
+ * Constructs a new {@link MethodAdapter} object.
+ *
+ * @param mv the code visitor to which this adapter must delegate calls.
+ */
+ public MethodAdapter(final MethodVisitor mv) {
+ this.mv = mv;
+ }
+
+ public AnnotationVisitor visitAnnotationDefault() {
+ return mv.visitAnnotationDefault();
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ return mv.visitAnnotation(desc, visible);
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(
+ final int parameter,
+ final String desc,
+ final boolean visible)
+ {
+ return mv.visitParameterAnnotation(parameter, desc, visible);
+ }
+
+ public void visitAttribute(final Attribute attr) {
+ mv.visitAttribute(attr);
+ }
+
+ public void visitCode() {
+ mv.visitCode();
+ }
+
+ public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack)
+ {
+ mv.visitFrame(type, nLocal, local, nStack, stack);
+ }
+
+ public void visitInsn(final int opcode) {
+ mv.visitInsn(opcode);
+ }
+
+ public void visitIntInsn(final int opcode, final int operand) {
+ mv.visitIntInsn(opcode, operand);
+ }
+
+ public void visitVarInsn(final int opcode, final int var) {
+ mv.visitVarInsn(opcode, var);
+ }
+
+ public void visitTypeInsn(final int opcode, final String type) {
+ mv.visitTypeInsn(opcode, type);
+ }
+
+ public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ mv.visitFieldInsn(opcode, owner, name, desc);
+ }
+
+ public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ mv.visitMethodInsn(opcode, owner, name, desc);
+ }
+
+ public void visitJumpInsn(final int opcode, final Label label) {
+ mv.visitJumpInsn(opcode, label);
+ }
+
+ public void visitLabel(final Label label) {
+ mv.visitLabel(label);
+ }
+
+ public void visitLdcInsn(final Object cst) {
+ mv.visitLdcInsn(cst);
+ }
+
+ public void visitIincInsn(final int var, final int increment) {
+ mv.visitIincInsn(var, increment);
+ }
+
+ public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label[] labels)
+ {
+ mv.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+
+ public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int[] keys,
+ final Label[] labels)
+ {
+ mv.visitLookupSwitchInsn(dflt, keys, labels);
+ }
+
+ public void visitMultiANewArrayInsn(final String desc, final int dims) {
+ mv.visitMultiANewArrayInsn(desc, dims);
+ }
+
+ public void visitTryCatchBlock(
+ final Label start,
+ final Label end,
+ final Label handler,
+ final String type)
+ {
+ mv.visitTryCatchBlock(start, end, handler, type);
+ }
+
+ public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index)
+ {
+ mv.visitLocalVariable(name, desc, signature, start, end, index);
+ }
+
+ public void visitLineNumber(final int line, final Label start) {
+ mv.visitLineNumber(line, start);
+ }
+
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ mv.visitMaxs(maxStack, maxLocals);
+ }
+
+ public void visitEnd() {
+ mv.visitEnd();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/MethodVisitor.java b/cglib-and-asm/src/org/mockito/asm/MethodVisitor.java
new file mode 100644
index 0000000..ee2a795
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/MethodVisitor.java
@@ -0,0 +1,395 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * A visitor to visit a Java method. The methods of this interface must be
+ * called in the following order: [ <tt>visitAnnotationDefault</tt> ] (
+ * <tt>visitAnnotation</tt> | <tt>visitParameterAnnotation</tt> |
+ * <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> |
+ * <tt>visit</tt><i>X</i>Insn</tt> | <tt>visitLabel</tt> | <tt>visitTryCatchBlock</tt> |
+ * <tt>visitLocalVariable</tt> | <tt>visitLineNumber</tt>)* <tt>visitMaxs</tt> ]
+ * <tt>visitEnd</tt>. In addition, the <tt>visit</tt><i>X</i>Insn</tt>
+ * and <tt>visitLabel</tt> methods must be called in the sequential order of
+ * the bytecode instructions of the visited code, <tt>visitTryCatchBlock</tt>
+ * must be called <i>before</i> the labels passed as arguments have been
+ * visited, and the <tt>visitLocalVariable</tt> and <tt>visitLineNumber</tt>
+ * methods must be called <i>after</i> the labels passed as arguments have been
+ * visited.
+ *
+ * @author Eric Bruneton
+ */
+public interface MethodVisitor {
+
+ // -------------------------------------------------------------------------
+ // Annotations and non standard attributes
+ // -------------------------------------------------------------------------
+
+ /**
+ * Visits the default value of this annotation interface method.
+ *
+ * @return a visitor to the visit the actual default value of this
+ * annotation interface method, or <tt>null</tt> if this visitor
+ * is not interested in visiting this default value. The 'name'
+ * parameters passed to the methods of this annotation visitor are
+ * ignored. Moreover, exacly one visit method must be called on this
+ * annotation visitor, followed by visitEnd.
+ */
+ AnnotationVisitor visitAnnotationDefault();
+
+ /**
+ * Visits an annotation of this method.
+ *
+ * @param desc the class descriptor of the annotation class.
+ * @param visible <tt>true</tt> if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or <tt>null</tt> if
+ * this visitor is not interested in visiting this annotation.
+ */
+ AnnotationVisitor visitAnnotation(String desc, boolean visible);
+
+ /**
+ * Visits an annotation of a parameter this method.
+ *
+ * @param parameter the parameter index.
+ * @param desc the class descriptor of the annotation class.
+ * @param visible <tt>true</tt> if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or <tt>null</tt> if
+ * this visitor is not interested in visiting this annotation.
+ */
+ AnnotationVisitor visitParameterAnnotation(
+ int parameter,
+ String desc,
+ boolean visible);
+
+ /**
+ * Visits a non standard attribute of this method.
+ *
+ * @param attr an attribute.
+ */
+ void visitAttribute(Attribute attr);
+
+ /**
+ * Starts the visit of the method's code, if any (i.e. non abstract method).
+ */
+ void visitCode();
+
+ /**
+ * Visits the current state of the local variables and operand stack
+ * elements. This method must(*) be called <i>just before</i> any
+ * instruction <b>i</b> that follows an unconditionnal branch instruction
+ * such as GOTO or THROW, that is the target of a jump instruction, or that
+ * starts an exception handler block. The visited types must describe the
+ * values of the local variables and of the operand stack elements <i>just
+ * before</i> <b>i</b> is executed. <br> <br> (*) this is mandatory only
+ * for classes whose version is greater than or equal to
+ * {@link Opcodes#V1_6 V1_6}. <br> <br> Packed frames are basically
+ * "deltas" from the state of the previous frame (very first frame is
+ * implicitly defined by the method's parameters and access flags): <ul>
+ * <li>{@link Opcodes#F_SAME} representing frame with exactly the same
+ * locals as the previous frame and with the empty stack.</li> <li>{@link Opcodes#F_SAME1}
+ * representing frame with exactly the same locals as the previous frame and
+ * with single value on the stack (<code>nStack</code> is 1 and
+ * <code>stack[0]</code> contains value for the type of the stack item).</li>
+ * <li>{@link Opcodes#F_APPEND} representing frame with current locals are
+ * the same as the locals in the previous frame, except that additional
+ * locals are defined (<code>nLocal</code> is 1, 2 or 3 and
+ * <code>local</code> elements contains values representing added types).</li>
+ * <li>{@link Opcodes#F_CHOP} representing frame with current locals are
+ * the same as the locals in the previous frame, except that the last 1-3
+ * locals are absent and with the empty stack (<code>nLocals</code> is 1,
+ * 2 or 3). </li> <li>{@link Opcodes#F_FULL} representing complete frame
+ * data.</li> </li> </ul>
+ *
+ * @param type the type of this stack map frame. Must be
+ * {@link Opcodes#F_NEW} for expanded frames, or
+ * {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND},
+ * {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or
+ * {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed
+ * frames.
+ * @param nLocal the number of local variables in the visited frame.
+ * @param local the local variable types in this frame. This array must not
+ * be modified. Primitive types are represented by
+ * {@link Opcodes#TOP}, {@link Opcodes#INTEGER},
+ * {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
+ * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
+ * {@link Opcodes#UNINITIALIZED_THIS} (long and double are
+ * represented by a single element). Reference types are represented
+ * by String objects (representing internal names), and uninitialized
+ * types by Label objects (this label designates the NEW instruction
+ * that created this uninitialized value).
+ * @param nStack the number of operand stack elements in the visited frame.
+ * @param stack the operand stack types in this frame. This array must not
+ * be modified. Its content has the same format as the "local" array.
+ */
+ void visitFrame(
+ int type,
+ int nLocal,
+ Object[] local,
+ int nStack,
+ Object[] stack);
+
+ // -------------------------------------------------------------------------
+ // Normal instructions
+ // -------------------------------------------------------------------------
+
+ /**
+ * Visits a zero operand instruction.
+ *
+ * @param opcode the opcode of the instruction to be visited. This opcode is
+ * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2,
+ * ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0,
+ * FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, LALOAD, FALOAD,
+ * DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE,
+ * DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP,
+ * DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, IADD, LADD, FADD,
+ * DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV,
+ * FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL,
+ * LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR,
+ * I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B,
+ * I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN,
+ * FRETURN, DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW,
+ * MONITORENTER, or MONITOREXIT.
+ */
+ void visitInsn(int opcode);
+
+ /**
+ * Visits an instruction with a single int operand.
+ *
+ * @param opcode the opcode of the instruction to be visited. This opcode is
+ * either BIPUSH, SIPUSH or NEWARRAY.
+ * @param operand the operand of the instruction to be visited.<br> When
+ * opcode is BIPUSH, operand value should be between Byte.MIN_VALUE
+ * and Byte.MAX_VALUE.<br> When opcode is SIPUSH, operand value
+ * should be between Short.MIN_VALUE and Short.MAX_VALUE.<br> When
+ * opcode is NEWARRAY, operand value should be one of
+ * {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR},
+ * {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE},
+ * {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT},
+ * {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}.
+ */
+ void visitIntInsn(int opcode, int operand);
+
+ /**
+ * Visits a local variable instruction. A local variable instruction is an
+ * instruction that loads or stores the value of a local variable.
+ *
+ * @param opcode the opcode of the local variable instruction to be visited.
+ * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE,
+ * LSTORE, FSTORE, DSTORE, ASTORE or RET.
+ * @param var the operand of the instruction to be visited. This operand is
+ * the index of a local variable.
+ */
+ void visitVarInsn(int opcode, int var);
+
+ /**
+ * Visits a type instruction. A type instruction is an instruction that
+ * takes the internal name of a class as parameter.
+ *
+ * @param opcode the opcode of the type instruction to be visited. This
+ * opcode is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF.
+ * @param type the operand of the instruction to be visited. This operand
+ * must be the internal name of an object or array class (see {@link
+ * Type#getInternalName() getInternalName}).
+ */
+ void visitTypeInsn(int opcode, String type);
+
+ /**
+ * Visits a field instruction. A field instruction is an instruction that
+ * loads or stores the value of a field of an object.
+ *
+ * @param opcode the opcode of the type instruction to be visited. This
+ * opcode is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
+ * @param owner the internal name of the field's owner class (see {@link
+ * Type#getInternalName() getInternalName}).
+ * @param name the field's name.
+ * @param desc the field's descriptor (see {@link Type Type}).
+ */
+ void visitFieldInsn(int opcode, String owner, String name, String desc);
+
+ /**
+ * Visits a method instruction. A method instruction is an instruction that
+ * invokes a method.
+ *
+ * @param opcode the opcode of the type instruction to be visited. This
+ * opcode is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or
+ * INVOKEINTERFACE.
+ * @param owner the internal name of the method's owner class (see {@link
+ * Type#getInternalName() getInternalName}).
+ * @param name the method's name.
+ * @param desc the method's descriptor (see {@link Type Type}).
+ */
+ void visitMethodInsn(int opcode, String owner, String name, String desc);
+
+ /**
+ * Visits a jump instruction. A jump instruction is an instruction that may
+ * jump to another instruction.
+ *
+ * @param opcode the opcode of the type instruction to be visited. This
+ * opcode is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ,
+ * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ,
+ * IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL.
+ * @param label the operand of the instruction to be visited. This operand
+ * is a label that designates the instruction to which the jump
+ * instruction may jump.
+ */
+ void visitJumpInsn(int opcode, Label label);
+
+ /**
+ * Visits a label. A label designates the instruction that will be visited
+ * just after it.
+ *
+ * @param label a {@link Label Label} object.
+ */
+ void visitLabel(Label label);
+
+ // -------------------------------------------------------------------------
+ // Special instructions
+ // -------------------------------------------------------------------------
+
+ /**
+ * Visits a LDC instruction.
+ *
+ * @param cst the constant to be loaded on the stack. This parameter must be
+ * a non null {@link Integer}, a {@link Float}, a {@link Long}, a
+ * {@link Double} a {@link String} (or a {@link Type} for
+ * <tt>.class</tt> constants, for classes whose version is 49.0 or
+ * more).
+ */
+ void visitLdcInsn(Object cst);
+
+ /**
+ * Visits an IINC instruction.
+ *
+ * @param var index of the local variable to be incremented.
+ * @param increment amount to increment the local variable by.
+ */
+ void visitIincInsn(int var, int increment);
+
+ /**
+ * Visits a TABLESWITCH instruction.
+ *
+ * @param min the minimum key value.
+ * @param max the maximum key value.
+ * @param dflt beginning of the default handler block.
+ * @param labels beginnings of the handler blocks. <tt>labels[i]</tt> is
+ * the beginning of the handler block for the <tt>min + i</tt> key.
+ */
+ void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels);
+
+ /**
+ * Visits a LOOKUPSWITCH instruction.
+ *
+ * @param dflt beginning of the default handler block.
+ * @param keys the values of the keys.
+ * @param labels beginnings of the handler blocks. <tt>labels[i]</tt> is
+ * the beginning of the handler block for the <tt>keys[i]</tt> key.
+ */
+ void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels);
+
+ /**
+ * Visits a MULTIANEWARRAY instruction.
+ *
+ * @param desc an array type descriptor (see {@link Type Type}).
+ * @param dims number of dimensions of the array to allocate.
+ */
+ void visitMultiANewArrayInsn(String desc, int dims);
+
+ // -------------------------------------------------------------------------
+ // Exceptions table entries, debug information, max stack and max locals
+ // -------------------------------------------------------------------------
+
+ /**
+ * Visits a try catch block.
+ *
+ * @param start beginning of the exception handler's scope (inclusive).
+ * @param end end of the exception handler's scope (exclusive).
+ * @param handler beginning of the exception handler's code.
+ * @param type internal name of the type of exceptions handled by the
+ * handler, or <tt>null</tt> to catch any exceptions (for "finally"
+ * blocks).
+ * @throws IllegalArgumentException if one of the labels has already been
+ * visited by this visitor (by the {@link #visitLabel visitLabel}
+ * method).
+ */
+ void visitTryCatchBlock(Label start, Label end, Label handler, String type);
+
+ /**
+ * Visits a local variable declaration.
+ *
+ * @param name the name of a local variable.
+ * @param desc the type descriptor of this local variable.
+ * @param signature the type signature of this local variable. May be
+ * <tt>null</tt> if the local variable type does not use generic
+ * types.
+ * @param start the first instruction corresponding to the scope of this
+ * local variable (inclusive).
+ * @param end the last instruction corresponding to the scope of this local
+ * variable (exclusive).
+ * @param index the local variable's index.
+ * @throws IllegalArgumentException if one of the labels has not already
+ * been visited by this visitor (by the
+ * {@link #visitLabel visitLabel} method).
+ */
+ void visitLocalVariable(
+ String name,
+ String desc,
+ String signature,
+ Label start,
+ Label end,
+ int index);
+
+ /**
+ * Visits a line number declaration.
+ *
+ * @param line a line number. This number refers to the source file from
+ * which the class was compiled.
+ * @param start the first instruction corresponding to this line number.
+ * @throws IllegalArgumentException if <tt>start</tt> has not already been
+ * visited by this visitor (by the {@link #visitLabel visitLabel}
+ * method).
+ */
+ void visitLineNumber(int line, Label start);
+
+ /**
+ * Visits the maximum stack size and the maximum number of local variables
+ * of the method.
+ *
+ * @param maxStack maximum stack size of the method.
+ * @param maxLocals maximum number of local variables for the method.
+ */
+ void visitMaxs(int maxStack, int maxLocals);
+
+ /**
+ * Visits the end of the method. This method, which is the last one to be
+ * called, is used to inform the visitor that all the annotations and
+ * attributes of the method have been visited.
+ */
+ void visitEnd();
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/MethodWriter.java b/cglib-and-asm/src/org/mockito/asm/MethodWriter.java
new file mode 100644
index 0000000..c920686
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/MethodWriter.java
@@ -0,0 +1,2601 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * A {@link MethodVisitor} that generates methods in bytecode form. Each visit
+ * method of this class appends the bytecode corresponding to the visited
+ * instruction to a byte vector, in the order these methods are called.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+class MethodWriter implements MethodVisitor {
+
+ /**
+ * Pseudo access flag used to denote constructors.
+ */
+ static final int ACC_CONSTRUCTOR = 262144;
+
+ /**
+ * Frame has exactly the same locals as the previous stack map frame and
+ * number of stack items is zero.
+ */
+ static final int SAME_FRAME = 0; // to 63 (0-3f)
+
+ /**
+ * Frame has exactly the same locals as the previous stack map frame and
+ * number of stack items is 1
+ */
+ static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)
+
+ /**
+ * Reserved for future use
+ */
+ static final int RESERVED = 128;
+
+ /**
+ * Frame has exactly the same locals as the previous stack map frame and
+ * number of stack items is 1. Offset is bigger then 63;
+ */
+ static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7
+
+ /**
+ * Frame where current locals are the same as the locals in the previous
+ * frame, except that the k last locals are absent. The value of k is given
+ * by the formula 251-frame_type.
+ */
+ static final int CHOP_FRAME = 248; // to 250 (f8-fA)
+
+ /**
+ * Frame has exactly the same locals as the previous stack map frame and
+ * number of stack items is zero. Offset is bigger then 63;
+ */
+ static final int SAME_FRAME_EXTENDED = 251; // fb
+
+ /**
+ * Frame where current locals are the same as the locals in the previous
+ * frame, except that k additional locals are defined. The value of k is
+ * given by the formula frame_type-251.
+ */
+ static final int APPEND_FRAME = 252; // to 254 // fc-fe
+
+ /**
+ * Full frame
+ */
+ static final int FULL_FRAME = 255; // ff
+
+ /**
+ * Indicates that the stack map frames must be recomputed from scratch. In
+ * this case the maximum stack size and number of local variables is also
+ * recomputed from scratch.
+ *
+ * @see #compute
+ */
+ private static final int FRAMES = 0;
+
+ /**
+ * Indicates that the maximum stack size and number of local variables must
+ * be automatically computed.
+ *
+ * @see #compute
+ */
+ private static final int MAXS = 1;
+
+ /**
+ * Indicates that nothing must be automatically computed.
+ *
+ * @see #compute
+ */
+ private static final int NOTHING = 2;
+
+ /**
+ * Next method writer (see {@link ClassWriter#firstMethod firstMethod}).
+ */
+ MethodWriter next;
+
+ /**
+ * The class writer to which this method must be added.
+ */
+ final ClassWriter cw;
+
+ /**
+ * Access flags of this method.
+ */
+ private int access;
+
+ /**
+ * The index of the constant pool item that contains the name of this
+ * method.
+ */
+ private final int name;
+
+ /**
+ * The index of the constant pool item that contains the descriptor of this
+ * method.
+ */
+ private final int desc;
+
+ /**
+ * The descriptor of this method.
+ */
+ private final String descriptor;
+
+ /**
+ * The signature of this method.
+ */
+ String signature;
+
+ /**
+ * If not zero, indicates that the code of this method must be copied from
+ * the ClassReader associated to this writer in <code>cw.cr</code>. More
+ * precisely, this field gives the index of the first byte to copied from
+ * <code>cw.cr.b</code>.
+ */
+ int classReaderOffset;
+
+ /**
+ * If not zero, indicates that the code of this method must be copied from
+ * the ClassReader associated to this writer in <code>cw.cr</code>. More
+ * precisely, this field gives the number of bytes to copied from
+ * <code>cw.cr.b</code>.
+ */
+ int classReaderLength;
+
+ /**
+ * Number of exceptions that can be thrown by this method.
+ */
+ int exceptionCount;
+
+ /**
+ * The exceptions that can be thrown by this method. More precisely, this
+ * array contains the indexes of the constant pool items that contain the
+ * internal names of these exception classes.
+ */
+ int[] exceptions;
+
+ /**
+ * The annotation default attribute of this method. May be <tt>null</tt>.
+ */
+ private ByteVector annd;
+
+ /**
+ * The runtime visible annotations of this method. May be <tt>null</tt>.
+ */
+ private AnnotationWriter anns;
+
+ /**
+ * The runtime invisible annotations of this method. May be <tt>null</tt>.
+ */
+ private AnnotationWriter ianns;
+
+ /**
+ * The runtime visible parameter annotations of this method. May be
+ * <tt>null</tt>.
+ */
+ private AnnotationWriter[] panns;
+
+ /**
+ * The runtime invisible parameter annotations of this method. May be
+ * <tt>null</tt>.
+ */
+ private AnnotationWriter[] ipanns;
+
+ /**
+ * The number of synthetic parameters of this method.
+ */
+ private int synthetics;
+
+ /**
+ * The non standard attributes of the method.
+ */
+ private Attribute attrs;
+
+ /**
+ * The bytecode of this method.
+ */
+ private ByteVector code = new ByteVector();
+
+ /**
+ * Maximum stack size of this method.
+ */
+ private int maxStack;
+
+ /**
+ * Maximum number of local variables for this method.
+ */
+ private int maxLocals;
+
+ /**
+ * Number of stack map frames in the StackMapTable attribute.
+ */
+ private int frameCount;
+
+ /**
+ * The StackMapTable attribute.
+ */
+ private ByteVector stackMap;
+
+ /**
+ * The offset of the last frame that was written in the StackMapTable
+ * attribute.
+ */
+ private int previousFrameOffset;
+
+ /**
+ * The last frame that was written in the StackMapTable attribute.
+ *
+ * @see #frame
+ */
+ private int[] previousFrame;
+
+ /**
+ * Index of the next element to be added in {@link #frame}.
+ */
+ private int frameIndex;
+
+ /**
+ * The current stack map frame. The first element contains the offset of the
+ * instruction to which the frame corresponds, the second element is the
+ * number of locals and the third one is the number of stack elements. The
+ * local variables start at index 3 and are followed by the operand stack
+ * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] =
+ * nStack, frame[3] = nLocal. All types are encoded as integers, with the
+ * same format as the one used in {@link Label}, but limited to BASE types.
+ */
+ private int[] frame;
+
+ /**
+ * Number of elements in the exception handler list.
+ */
+ private int handlerCount;
+
+ /**
+ * The first element in the exception handler list.
+ */
+ private Handler firstHandler;
+
+ /**
+ * The last element in the exception handler list.
+ */
+ private Handler lastHandler;
+
+ /**
+ * Number of entries in the LocalVariableTable attribute.
+ */
+ private int localVarCount;
+
+ /**
+ * The LocalVariableTable attribute.
+ */
+ private ByteVector localVar;
+
+ /**
+ * Number of entries in the LocalVariableTypeTable attribute.
+ */
+ private int localVarTypeCount;
+
+ /**
+ * The LocalVariableTypeTable attribute.
+ */
+ private ByteVector localVarType;
+
+ /**
+ * Number of entries in the LineNumberTable attribute.
+ */
+ private int lineNumberCount;
+
+ /**
+ * The LineNumberTable attribute.
+ */
+ private ByteVector lineNumber;
+
+ /**
+ * The non standard attributes of the method's code.
+ */
+ private Attribute cattrs;
+
+ /**
+ * Indicates if some jump instructions are too small and need to be resized.
+ */
+ private boolean resize;
+
+ /**
+ * The number of subroutines in this method.
+ */
+ private int subroutines;
+
+ // ------------------------------------------------------------------------
+
+ /*
+ * Fields for the control flow graph analysis algorithm (used to compute the
+ * maximum stack size). A control flow graph contains one node per "basic
+ * block", and one edge per "jump" from one basic block to another. Each
+ * node (i.e., each basic block) is represented by the Label object that
+ * corresponds to the first instruction of this basic block. Each node also
+ * stores the list of its successors in the graph, as a linked list of Edge
+ * objects.
+ */
+
+ /**
+ * Indicates what must be automatically computed.
+ *
+ * @see #FRAMES
+ * @see #MAXS
+ * @see #NOTHING
+ */
+ private final int compute;
+
+ /**
+ * A list of labels. This list is the list of basic blocks in the method,
+ * i.e. a list of Label objects linked to each other by their
+ * {@link Label#successor} field, in the order they are visited by
+ * {@link MethodVisitor#visitLabel}, and starting with the first basic block.
+ */
+ private Label labels;
+
+ /**
+ * The previous basic block.
+ */
+ private Label previousBlock;
+
+ /**
+ * The current basic block.
+ */
+ private Label currentBlock;
+
+ /**
+ * The (relative) stack size after the last visited instruction. This size
+ * is relative to the beginning of the current basic block, i.e., the true
+ * stack size after the last visited instruction is equal to the
+ * {@link Label#inputStackTop beginStackSize} of the current basic block
+ * plus <tt>stackSize</tt>.
+ */
+ private int stackSize;
+
+ /**
+ * The (relative) maximum stack size after the last visited instruction.
+ * This size is relative to the beginning of the current basic block, i.e.,
+ * the true maximum stack size after the last visited instruction is equal
+ * to the {@link Label#inputStackTop beginStackSize} of the current basic
+ * block plus <tt>stackSize</tt>.
+ */
+ private int maxStackSize;
+
+ // ------------------------------------------------------------------------
+ // Constructor
+ // ------------------------------------------------------------------------
+
+ /**
+ * Constructs a new {@link MethodWriter}.
+ *
+ * @param cw the class writer in which the method must be added.
+ * @param access the method's access flags (see {@link Opcodes}).
+ * @param name the method's name.
+ * @param desc the method's descriptor (see {@link Type}).
+ * @param signature the method's signature. May be <tt>null</tt>.
+ * @param exceptions the internal names of the method's exceptions. May be
+ * <tt>null</tt>.
+ * @param computeMaxs <tt>true</tt> if the maximum stack size and number
+ * of local variables must be automatically computed.
+ * @param computeFrames <tt>true</tt> if the stack map tables must be
+ * recomputed from scratch.
+ */
+ MethodWriter(
+ final ClassWriter cw,
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions,
+ final boolean computeMaxs,
+ final boolean computeFrames)
+ {
+ if (cw.firstMethod == null) {
+ cw.firstMethod = this;
+ } else {
+ cw.lastMethod.next = this;
+ }
+ cw.lastMethod = this;
+ this.cw = cw;
+ this.access = access;
+ this.name = cw.newUTF8(name);
+ this.desc = cw.newUTF8(desc);
+ this.descriptor = desc;
+ if (ClassReader.SIGNATURES) {
+ this.signature = signature;
+ }
+ if (exceptions != null && exceptions.length > 0) {
+ exceptionCount = exceptions.length;
+ this.exceptions = new int[exceptionCount];
+ for (int i = 0; i < exceptionCount; ++i) {
+ this.exceptions[i] = cw.newClass(exceptions[i]);
+ }
+ }
+ this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING);
+ if (computeMaxs || computeFrames) {
+ if (computeFrames && "<init>".equals(name)) {
+ this.access |= ACC_CONSTRUCTOR;
+ }
+ // updates maxLocals
+ int size = getArgumentsAndReturnSizes(descriptor) >> 2;
+ if ((access & Opcodes.ACC_STATIC) != 0) {
+ --size;
+ }
+ maxLocals = size;
+ // creates and visits the label for the first basic block
+ labels = new Label();
+ labels.status |= Label.PUSHED;
+ visitLabel(labels);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the MethodVisitor interface
+ // ------------------------------------------------------------------------
+
+ public AnnotationVisitor visitAnnotationDefault() {
+ if (!ClassReader.ANNOTATIONS) {
+ return null;
+ }
+ annd = new ByteVector();
+ return new AnnotationWriter(cw, false, annd, null, 0);
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ if (!ClassReader.ANNOTATIONS) {
+ return null;
+ }
+ ByteVector bv = new ByteVector();
+ // write type, and reserve space for values count
+ bv.putShort(cw.newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
+ if (visible) {
+ aw.next = anns;
+ anns = aw;
+ } else {
+ aw.next = ianns;
+ ianns = aw;
+ }
+ return aw;
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(
+ final int parameter,
+ final String desc,
+ final boolean visible)
+ {
+ if (!ClassReader.ANNOTATIONS) {
+ return null;
+ }
+ ByteVector bv = new ByteVector();
+ if ("Ljava/lang/Synthetic;".equals(desc)) {
+ // workaround for a bug in javac with synthetic parameters
+ // see ClassReader.readParameterAnnotations
+ synthetics = Math.max(synthetics, parameter + 1);
+ return new AnnotationWriter(cw, false, bv, null, 0);
+ }
+ // write type, and reserve space for values count
+ bv.putShort(cw.newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
+ if (visible) {
+ if (panns == null) {
+ panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
+ }
+ aw.next = panns[parameter];
+ panns[parameter] = aw;
+ } else {
+ if (ipanns == null) {
+ ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
+ }
+ aw.next = ipanns[parameter];
+ ipanns[parameter] = aw;
+ }
+ return aw;
+ }
+
+ public void visitAttribute(final Attribute attr) {
+ if (attr.isCodeAttribute()) {
+ attr.next = cattrs;
+ cattrs = attr;
+ } else {
+ attr.next = attrs;
+ attrs = attr;
+ }
+ }
+
+ public void visitCode() {
+ }
+
+ public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack)
+ {
+ if (!ClassReader.FRAMES || compute == FRAMES) {
+ return;
+ }
+
+ if (type == Opcodes.F_NEW) {
+ startFrame(code.length, nLocal, nStack);
+ for (int i = 0; i < nLocal; ++i) {
+ if (local[i] instanceof String) {
+ frame[frameIndex++] = Frame.OBJECT
+ | cw.addType((String) local[i]);
+ } else if (local[i] instanceof Integer) {
+ frame[frameIndex++] = ((Integer) local[i]).intValue();
+ } else {
+ frame[frameIndex++] = Frame.UNINITIALIZED
+ | cw.addUninitializedType("",
+ ((Label) local[i]).position);
+ }
+ }
+ for (int i = 0; i < nStack; ++i) {
+ if (stack[i] instanceof String) {
+ frame[frameIndex++] = Frame.OBJECT
+ | cw.addType((String) stack[i]);
+ } else if (stack[i] instanceof Integer) {
+ frame[frameIndex++] = ((Integer) stack[i]).intValue();
+ } else {
+ frame[frameIndex++] = Frame.UNINITIALIZED
+ | cw.addUninitializedType("",
+ ((Label) stack[i]).position);
+ }
+ }
+ endFrame();
+ } else {
+ int delta;
+ if (stackMap == null) {
+ stackMap = new ByteVector();
+ delta = code.length;
+ } else {
+ delta = code.length - previousFrameOffset - 1;
+ }
+
+ switch (type) {
+ case Opcodes.F_FULL:
+ stackMap.putByte(FULL_FRAME)
+ .putShort(delta)
+ .putShort(nLocal);
+ for (int i = 0; i < nLocal; ++i) {
+ writeFrameType(local[i]);
+ }
+ stackMap.putShort(nStack);
+ for (int i = 0; i < nStack; ++i) {
+ writeFrameType(stack[i]);
+ }
+ break;
+ case Opcodes.F_APPEND:
+ stackMap.putByte(SAME_FRAME_EXTENDED + nLocal)
+ .putShort(delta);
+ for (int i = 0; i < nLocal; ++i) {
+ writeFrameType(local[i]);
+ }
+ break;
+ case Opcodes.F_CHOP:
+ stackMap.putByte(SAME_FRAME_EXTENDED - nLocal)
+ .putShort(delta);
+ break;
+ case Opcodes.F_SAME:
+ if (delta < 64) {
+ stackMap.putByte(delta);
+ } else {
+ stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
+ }
+ break;
+ case Opcodes.F_SAME1:
+ if (delta < 64) {
+ stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
+ } else {
+ stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
+ .putShort(delta);
+ }
+ writeFrameType(stack[0]);
+ break;
+ }
+
+ previousFrameOffset = code.length;
+ ++frameCount;
+ }
+ }
+
+ public void visitInsn(final int opcode) {
+ // adds the instruction to the bytecode of the method
+ code.putByte(opcode);
+ // update currentBlock
+ // Label currentBlock = this.currentBlock;
+ if (currentBlock != null) {
+ if (compute == FRAMES) {
+ currentBlock.frame.execute(opcode, 0, null, null);
+ } else {
+ // updates current and max stack sizes
+ int size = stackSize + Frame.SIZE[opcode];
+ if (size > maxStackSize) {
+ maxStackSize = size;
+ }
+ stackSize = size;
+ }
+ // if opcode == ATHROW or xRETURN, ends current block (no successor)
+ if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
+ || opcode == Opcodes.ATHROW)
+ {
+ noSuccessor();
+ }
+ }
+ }
+
+ public void visitIntInsn(final int opcode, final int operand) {
+ // Label currentBlock = this.currentBlock;
+ if (currentBlock != null) {
+ if (compute == FRAMES) {
+ currentBlock.frame.execute(opcode, operand, null, null);
+ } else if (opcode != Opcodes.NEWARRAY) {
+ // updates current and max stack sizes only for NEWARRAY
+ // (stack size variation = 0 for BIPUSH or SIPUSH)
+ int size = stackSize + 1;
+ if (size > maxStackSize) {
+ maxStackSize = size;
+ }
+ stackSize = size;
+ }
+ }
+ // adds the instruction to the bytecode of the method
+ if (opcode == Opcodes.SIPUSH) {
+ code.put12(opcode, operand);
+ } else { // BIPUSH or NEWARRAY
+ code.put11(opcode, operand);
+ }
+ }
+
+ public void visitVarInsn(final int opcode, final int var) {
+ // Label currentBlock = this.currentBlock;
+ if (currentBlock != null) {
+ if (compute == FRAMES) {
+ currentBlock.frame.execute(opcode, var, null, null);
+ } else {
+ // updates current and max stack sizes
+ if (opcode == Opcodes.RET) {
+ // no stack change, but end of current block (no successor)
+ currentBlock.status |= Label.RET;
+ // save 'stackSize' here for future use
+ // (see {@link #findSubroutineSuccessors})
+ currentBlock.inputStackTop = stackSize;
+ noSuccessor();
+ } else { // xLOAD or xSTORE
+ int size = stackSize + Frame.SIZE[opcode];
+ if (size > maxStackSize) {
+ maxStackSize = size;
+ }
+ stackSize = size;
+ }
+ }
+ }
+ if (compute != NOTHING) {
+ // updates max locals
+ int n;
+ if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD
+ || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE)
+ {
+ n = var + 2;
+ } else {
+ n = var + 1;
+ }
+ if (n > maxLocals) {
+ maxLocals = n;
+ }
+ }
+ // adds the instruction to the bytecode of the method
+ if (var < 4 && opcode != Opcodes.RET) {
+ int opt;
+ if (opcode < Opcodes.ISTORE) {
+ /* ILOAD_0 */
+ opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;
+ } else {
+ /* ISTORE_0 */
+ opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;
+ }
+ code.putByte(opt);
+ } else if (var >= 256) {
+ code.putByte(196 /* WIDE */).put12(opcode, var);
+ } else {
+ code.put11(opcode, var);
+ }
+ if (opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) {
+ visitLabel(new Label());
+ }
+ }
+
+ public void visitTypeInsn(final int opcode, final String type) {
+ Item i = cw.newClassItem(type);
+ // Label currentBlock = this.currentBlock;
+ if (currentBlock != null) {
+ if (compute == FRAMES) {
+ currentBlock.frame.execute(opcode, code.length, cw, i);
+ } else if (opcode == Opcodes.NEW) {
+ // updates current and max stack sizes only if opcode == NEW
+ // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF)
+ int size = stackSize + 1;
+ if (size > maxStackSize) {
+ maxStackSize = size;
+ }
+ stackSize = size;
+ }
+ }
+ // adds the instruction to the bytecode of the method
+ code.put12(opcode, i.index);
+ }
+
+ public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ Item i = cw.newFieldItem(owner, name, desc);
+ // Label currentBlock = this.currentBlock;
+ if (currentBlock != null) {
+ if (compute == FRAMES) {
+ currentBlock.frame.execute(opcode, 0, cw, i);
+ } else {
+ int size;
+ // computes the stack size variation
+ char c = desc.charAt(0);
+ switch (opcode) {
+ case Opcodes.GETSTATIC:
+ size = stackSize + (c == 'D' || c == 'J' ? 2 : 1);
+ break;
+ case Opcodes.PUTSTATIC:
+ size = stackSize + (c == 'D' || c == 'J' ? -2 : -1);
+ break;
+ case Opcodes.GETFIELD:
+ size = stackSize + (c == 'D' || c == 'J' ? 1 : 0);
+ break;
+ // case Constants.PUTFIELD:
+ default:
+ size = stackSize + (c == 'D' || c == 'J' ? -3 : -2);
+ break;
+ }
+ // updates current and max stack sizes
+ if (size > maxStackSize) {
+ maxStackSize = size;
+ }
+ stackSize = size;
+ }
+ }
+ // adds the instruction to the bytecode of the method
+ code.put12(opcode, i.index);
+ }
+
+ public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ boolean itf = opcode == Opcodes.INVOKEINTERFACE;
+ Item i = cw.newMethodItem(owner, name, desc, itf);
+ int argSize = i.intVal;
+ // Label currentBlock = this.currentBlock;
+ if (currentBlock != null) {
+ if (compute == FRAMES) {
+ currentBlock.frame.execute(opcode, 0, cw, i);
+ } else {
+ /*
+ * computes the stack size variation. In order not to recompute
+ * several times this variation for the same Item, we use the
+ * intVal field of this item to store this variation, once it
+ * has been computed. More precisely this intVal field stores
+ * the sizes of the arguments and of the return value
+ * corresponding to desc.
+ */
+ if (argSize == 0) {
+ // the above sizes have not been computed yet,
+ // so we compute them...
+ argSize = getArgumentsAndReturnSizes(desc);
+ // ... and we save them in order
+ // not to recompute them in the future
+ i.intVal = argSize;
+ }
+ int size;
+ if (opcode == Opcodes.INVOKESTATIC) {
+ size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
+ } else {
+ size = stackSize - (argSize >> 2) + (argSize & 0x03);
+ }
+ // updates current and max stack sizes
+ if (size > maxStackSize) {
+ maxStackSize = size;
+ }
+ stackSize = size;
+ }
+ }
+ // adds the instruction to the bytecode of the method
+ if (itf) {
+ if (argSize == 0) {
+ argSize = getArgumentsAndReturnSizes(desc);
+ i.intVal = argSize;
+ }
+ code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
+ } else {
+ code.put12(opcode, i.index);
+ }
+ }
+
+ public void visitJumpInsn(final int opcode, final Label label) {
+ Label nextInsn = null;
+ // Label currentBlock = this.currentBlock;
+ if (currentBlock != null) {
+ if (compute == FRAMES) {
+ currentBlock.frame.execute(opcode, 0, null, null);
+ // 'label' is the target of a jump instruction
+ label.getFirst().status |= Label.TARGET;
+ // adds 'label' as a successor of this basic block
+ addSuccessor(Edge.NORMAL, label);
+ if (opcode != Opcodes.GOTO) {
+ // creates a Label for the next basic block
+ nextInsn = new Label();
+ }
+ } else {
+ if (opcode == Opcodes.JSR) {
+ if ((label.status & Label.SUBROUTINE) == 0) {
+ label.status |= Label.SUBROUTINE;
+ ++subroutines;
+ }
+ currentBlock.status |= Label.JSR;
+ addSuccessor(stackSize + 1, label);
+ // creates a Label for the next basic block
+ nextInsn = new Label();
+ /*
+ * note that, by construction in this method, a JSR block
+ * has at least two successors in the control flow graph:
+ * the first one leads the next instruction after the JSR,
+ * while the second one leads to the JSR target.
+ */
+ } else {
+ // updates current stack size (max stack size unchanged
+ // because stack size variation always negative in this
+ // case)
+ stackSize += Frame.SIZE[opcode];
+ addSuccessor(stackSize, label);
+ }
+ }
+ }
+ // adds the instruction to the bytecode of the method
+ if ((label.status & Label.RESOLVED) != 0
+ && label.position - code.length < Short.MIN_VALUE)
+ {
+ /*
+ * case of a backward jump with an offset < -32768. In this case we
+ * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx
+ * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the
+ * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'>
+ * designates the instruction just after the GOTO_W.
+ */
+ if (opcode == Opcodes.GOTO) {
+ code.putByte(200); // GOTO_W
+ } else if (opcode == Opcodes.JSR) {
+ code.putByte(201); // JSR_W
+ } else {
+ // if the IF instruction is transformed into IFNOT GOTO_W the
+ // next instruction becomes the target of the IFNOT instruction
+ if (nextInsn != null) {
+ nextInsn.status |= Label.TARGET;
+ }
+ code.putByte(opcode <= 166
+ ? ((opcode + 1) ^ 1) - 1
+ : opcode ^ 1);
+ code.putShort(8); // jump offset
+ code.putByte(200); // GOTO_W
+ }
+ label.put(this, code, code.length - 1, true);
+ } else {
+ /*
+ * case of a backward jump with an offset >= -32768, or of a forward
+ * jump with, of course, an unknown offset. In these cases we store
+ * the offset in 2 bytes (which will be increased in
+ * resizeInstructions, if needed).
+ */
+ code.putByte(opcode);
+ label.put(this, code, code.length - 1, false);
+ }
+ if (currentBlock != null) {
+ if (nextInsn != null) {
+ // if the jump instruction is not a GOTO, the next instruction
+ // is also a successor of this instruction. Calling visitLabel
+ // adds the label of this next instruction as a successor of the
+ // current block, and starts a new basic block
+ visitLabel(nextInsn);
+ }
+ if (opcode == Opcodes.GOTO) {
+ noSuccessor();
+ }
+ }
+ }
+
+ public void visitLabel(final Label label) {
+ // resolves previous forward references to label, if any
+ resize |= label.resolve(this, code.length, code.data);
+ // updates currentBlock
+ if ((label.status & Label.DEBUG) != 0) {
+ return;
+ }
+ if (compute == FRAMES) {
+ if (currentBlock != null) {
+ if (label.position == currentBlock.position) {
+ // successive labels, do not start a new basic block
+ currentBlock.status |= (label.status & Label.TARGET);
+ label.frame = currentBlock.frame;
+ return;
+ }
+ // ends current block (with one new successor)
+ addSuccessor(Edge.NORMAL, label);
+ }
+ // begins a new current block
+ currentBlock = label;
+ if (label.frame == null) {
+ label.frame = new Frame();
+ label.frame.owner = label;
+ }
+ // updates the basic block list
+ if (previousBlock != null) {
+ if (label.position == previousBlock.position) {
+ previousBlock.status |= (label.status & Label.TARGET);
+ label.frame = previousBlock.frame;
+ currentBlock = previousBlock;
+ return;
+ }
+ previousBlock.successor = label;
+ }
+ previousBlock = label;
+ } else if (compute == MAXS) {
+ if (currentBlock != null) {
+ // ends current block (with one new successor)
+ currentBlock.outputStackMax = maxStackSize;
+ addSuccessor(stackSize, label);
+ }
+ // begins a new current block
+ currentBlock = label;
+ // resets the relative current and max stack sizes
+ stackSize = 0;
+ maxStackSize = 0;
+ // updates the basic block list
+ if (previousBlock != null) {
+ previousBlock.successor = label;
+ }
+ previousBlock = label;
+ }
+ }
+
+ public void visitLdcInsn(final Object cst) {
+ Item i = cw.newConstItem(cst);
+ // Label currentBlock = this.currentBlock;
+ if (currentBlock != null) {
+ if (compute == FRAMES) {
+ currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
+ } else {
+ int size;
+ // computes the stack size variation
+ if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE)
+ {
+ size = stackSize + 2;
+ } else {
+ size = stackSize + 1;
+ }
+ // updates current and max stack sizes
+ if (size > maxStackSize) {
+ maxStackSize = size;
+ }
+ stackSize = size;
+ }
+ }
+ // adds the instruction to the bytecode of the method
+ int index = i.index;
+ if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) {
+ code.put12(20 /* LDC2_W */, index);
+ } else if (index >= 256) {
+ code.put12(19 /* LDC_W */, index);
+ } else {
+ code.put11(Opcodes.LDC, index);
+ }
+ }
+
+ public void visitIincInsn(final int var, final int increment) {
+ if (currentBlock != null) {
+ if (compute == FRAMES) {
+ currentBlock.frame.execute(Opcodes.IINC, var, null, null);
+ }
+ }
+ if (compute != NOTHING) {
+ // updates max locals
+ int n = var + 1;
+ if (n > maxLocals) {
+ maxLocals = n;
+ }
+ }
+ // adds the instruction to the bytecode of the method
+ if ((var > 255) || (increment > 127) || (increment < -128)) {
+ code.putByte(196 /* WIDE */)
+ .put12(Opcodes.IINC, var)
+ .putShort(increment);
+ } else {
+ code.putByte(Opcodes.IINC).put11(var, increment);
+ }
+ }
+
+ public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label[] labels)
+ {
+ // adds the instruction to the bytecode of the method
+ int source = code.length;
+ code.putByte(Opcodes.TABLESWITCH);
+ code.length += (4 - code.length % 4) % 4;
+ dflt.put(this, code, source, true);
+ code.putInt(min).putInt(max);
+ for (int i = 0; i < labels.length; ++i) {
+ labels[i].put(this, code, source, true);
+ }
+ // updates currentBlock
+ visitSwitchInsn(dflt, labels);
+ }
+
+ public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int[] keys,
+ final Label[] labels)
+ {
+ // adds the instruction to the bytecode of the method
+ int source = code.length;
+ code.putByte(Opcodes.LOOKUPSWITCH);
+ code.length += (4 - code.length % 4) % 4;
+ dflt.put(this, code, source, true);
+ code.putInt(labels.length);
+ for (int i = 0; i < labels.length; ++i) {
+ code.putInt(keys[i]);
+ labels[i].put(this, code, source, true);
+ }
+ // updates currentBlock
+ visitSwitchInsn(dflt, labels);
+ }
+
+ private void visitSwitchInsn(final Label dflt, final Label[] labels) {
+ // Label currentBlock = this.currentBlock;
+ if (currentBlock != null) {
+ if (compute == FRAMES) {
+ currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null);
+ // adds current block successors
+ addSuccessor(Edge.NORMAL, dflt);
+ dflt.getFirst().status |= Label.TARGET;
+ for (int i = 0; i < labels.length; ++i) {
+ addSuccessor(Edge.NORMAL, labels[i]);
+ labels[i].getFirst().status |= Label.TARGET;
+ }
+ } else {
+ // updates current stack size (max stack size unchanged)
+ --stackSize;
+ // adds current block successors
+ addSuccessor(stackSize, dflt);
+ for (int i = 0; i < labels.length; ++i) {
+ addSuccessor(stackSize, labels[i]);
+ }
+ }
+ // ends current block
+ noSuccessor();
+ }
+ }
+
+ public void visitMultiANewArrayInsn(final String desc, final int dims) {
+ Item i = cw.newClassItem(desc);
+ // Label currentBlock = this.currentBlock;
+ if (currentBlock != null) {
+ if (compute == FRAMES) {
+ currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i);
+ } else {
+ // updates current stack size (max stack size unchanged because
+ // stack size variation always negative or null)
+ stackSize += 1 - dims;
+ }
+ }
+ // adds the instruction to the bytecode of the method
+ code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims);
+ }
+
+ public void visitTryCatchBlock(
+ final Label start,
+ final Label end,
+ final Label handler,
+ final String type)
+ {
+ ++handlerCount;
+ Handler h = new Handler();
+ h.start = start;
+ h.end = end;
+ h.handler = handler;
+ h.desc = type;
+ h.type = type != null ? cw.newClass(type) : 0;
+ if (lastHandler == null) {
+ firstHandler = h;
+ } else {
+ lastHandler.next = h;
+ }
+ lastHandler = h;
+ }
+
+ public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index)
+ {
+ if (signature != null) {
+ if (localVarType == null) {
+ localVarType = new ByteVector();
+ }
+ ++localVarTypeCount;
+ localVarType.putShort(start.position)
+ .putShort(end.position - start.position)
+ .putShort(cw.newUTF8(name))
+ .putShort(cw.newUTF8(signature))
+ .putShort(index);
+ }
+ if (localVar == null) {
+ localVar = new ByteVector();
+ }
+ ++localVarCount;
+ localVar.putShort(start.position)
+ .putShort(end.position - start.position)
+ .putShort(cw.newUTF8(name))
+ .putShort(cw.newUTF8(desc))
+ .putShort(index);
+ if (compute != NOTHING) {
+ // updates max locals
+ char c = desc.charAt(0);
+ int n = index + (c == 'J' || c == 'D' ? 2 : 1);
+ if (n > maxLocals) {
+ maxLocals = n;
+ }
+ }
+ }
+
+ public void visitLineNumber(final int line, final Label start) {
+ if (lineNumber == null) {
+ lineNumber = new ByteVector();
+ }
+ ++lineNumberCount;
+ lineNumber.putShort(start.position);
+ lineNumber.putShort(line);
+ }
+
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ if (ClassReader.FRAMES && compute == FRAMES) {
+ // completes the control flow graph with exception handler blocks
+ Handler handler = firstHandler;
+ while (handler != null) {
+ Label l = handler.start.getFirst();
+ Label h = handler.handler.getFirst();
+ Label e = handler.end.getFirst();
+ // computes the kind of the edges to 'h'
+ String t = handler.desc == null
+ ? "java/lang/Throwable"
+ : handler.desc;
+ int kind = Frame.OBJECT | cw.addType(t);
+ // h is an exception handler
+ h.status |= Label.TARGET;
+ // adds 'h' as a successor of labels between 'start' and 'end'
+ while (l != e) {
+ // creates an edge to 'h'
+ Edge b = new Edge();
+ b.info = kind;
+ b.successor = h;
+ // adds it to the successors of 'l'
+ b.next = l.successors;
+ l.successors = b;
+ // goes to the next label
+ l = l.successor;
+ }
+ handler = handler.next;
+ }
+
+ // creates and visits the first (implicit) frame
+ Frame f = labels.frame;
+ Type[] args = Type.getArgumentTypes(descriptor);
+ f.initInputFrame(cw, access, args, this.maxLocals);
+ visitFrame(f);
+
+ /*
+ * fix point algorithm: mark the first basic block as 'changed'
+ * (i.e. put it in the 'changed' list) and, while there are changed
+ * basic blocks, choose one, mark it as unchanged, and update its
+ * successors (which can be changed in the process).
+ */
+ int max = 0;
+ Label changed = labels;
+ while (changed != null) {
+ // removes a basic block from the list of changed basic blocks
+ Label l = changed;
+ changed = changed.next;
+ l.next = null;
+ f = l.frame;
+ // a reacheable jump target must be stored in the stack map
+ if ((l.status & Label.TARGET) != 0) {
+ l.status |= Label.STORE;
+ }
+ // all visited labels are reacheable, by definition
+ l.status |= Label.REACHABLE;
+ // updates the (absolute) maximum stack size
+ int blockMax = f.inputStack.length + l.outputStackMax;
+ if (blockMax > max) {
+ max = blockMax;
+ }
+ // updates the successors of the current basic block
+ Edge e = l.successors;
+ while (e != null) {
+ Label n = e.successor.getFirst();
+ boolean change = f.merge(cw, n.frame, e.info);
+ if (change && n.next == null) {
+ // if n has changed and is not already in the 'changed'
+ // list, adds it to this list
+ n.next = changed;
+ changed = n;
+ }
+ e = e.next;
+ }
+ }
+ this.maxStack = max;
+
+ // visits all the frames that must be stored in the stack map
+ Label l = labels;
+ while (l != null) {
+ f = l.frame;
+ if ((l.status & Label.STORE) != 0) {
+ visitFrame(f);
+ }
+ if ((l.status & Label.REACHABLE) == 0) {
+ // finds start and end of dead basic block
+ Label k = l.successor;
+ int start = l.position;
+ int end = (k == null ? code.length : k.position) - 1;
+ // if non empty basic block
+ if (end >= start) {
+ // replaces instructions with NOP ... NOP ATHROW
+ for (int i = start; i < end; ++i) {
+ code.data[i] = Opcodes.NOP;
+ }
+ code.data[end] = (byte) Opcodes.ATHROW;
+ // emits a frame for this unreachable block
+ startFrame(start, 0, 1);
+ frame[frameIndex++] = Frame.OBJECT
+ | cw.addType("java/lang/Throwable");
+ endFrame();
+ }
+ }
+ l = l.successor;
+ }
+ } else if (compute == MAXS) {
+ // completes the control flow graph with exception handler blocks
+ Handler handler = firstHandler;
+ while (handler != null) {
+ Label l = handler.start;
+ Label h = handler.handler;
+ Label e = handler.end;
+ // adds 'h' as a successor of labels between 'start' and 'end'
+ while (l != e) {
+ // creates an edge to 'h'
+ Edge b = new Edge();
+ b.info = Edge.EXCEPTION;
+ b.successor = h;
+ // adds it to the successors of 'l'
+ if ((l.status & Label.JSR) == 0) {
+ b.next = l.successors;
+ l.successors = b;
+ } else {
+ // if l is a JSR block, adds b after the first two edges
+ // to preserve the hypothesis about JSR block successors
+ // order (see {@link #visitJumpInsn})
+ b.next = l.successors.next.next;
+ l.successors.next.next = b;
+ }
+ // goes to the next label
+ l = l.successor;
+ }
+ handler = handler.next;
+ }
+
+ if (subroutines > 0) {
+ // completes the control flow graph with the RET successors
+ /*
+ * first step: finds the subroutines. This step determines, for
+ * each basic block, to which subroutine(s) it belongs.
+ */
+ // finds the basic blocks that belong to the "main" subroutine
+ int id = 0;
+ labels.visitSubroutine(null, 1, subroutines);
+ // finds the basic blocks that belong to the real subroutines
+ Label l = labels;
+ while (l != null) {
+ if ((l.status & Label.JSR) != 0) {
+ // the subroutine is defined by l's TARGET, not by l
+ Label subroutine = l.successors.next.successor;
+ // if this subroutine has not been visited yet...
+ if ((subroutine.status & Label.VISITED) == 0) {
+ // ...assigns it a new id and finds its basic blocks
+ id += 1;
+ subroutine.visitSubroutine(null, (id / 32L) << 32
+ | (1L << (id % 32)), subroutines);
+ }
+ }
+ l = l.successor;
+ }
+ // second step: finds the successors of RET blocks
+ l = labels;
+ while (l != null) {
+ if ((l.status & Label.JSR) != 0) {
+ Label L = labels;
+ while (L != null) {
+ L.status &= ~Label.VISITED;
+ L = L.successor;
+ }
+ // the subroutine is defined by l's TARGET, not by l
+ Label subroutine = l.successors.next.successor;
+ subroutine.visitSubroutine(l, 0, subroutines);
+ }
+ l = l.successor;
+ }
+ }
+
+ /*
+ * control flow analysis algorithm: while the block stack is not
+ * empty, pop a block from this stack, update the max stack size,
+ * compute the true (non relative) begin stack size of the
+ * successors of this block, and push these successors onto the
+ * stack (unless they have already been pushed onto the stack).
+ * Note: by hypothesis, the {@link Label#inputStackTop} of the
+ * blocks in the block stack are the true (non relative) beginning
+ * stack sizes of these blocks.
+ */
+ int max = 0;
+ Label stack = labels;
+ while (stack != null) {
+ // pops a block from the stack
+ Label l = stack;
+ stack = stack.next;
+ // computes the true (non relative) max stack size of this block
+ int start = l.inputStackTop;
+ int blockMax = start + l.outputStackMax;
+ // updates the global max stack size
+ if (blockMax > max) {
+ max = blockMax;
+ }
+ // analyzes the successors of the block
+ Edge b = l.successors;
+ if ((l.status & Label.JSR) != 0) {
+ // ignores the first edge of JSR blocks (virtual successor)
+ b = b.next;
+ }
+ while (b != null) {
+ l = b.successor;
+ // if this successor has not already been pushed...
+ if ((l.status & Label.PUSHED) == 0) {
+ // computes its true beginning stack size...
+ l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start
+ + b.info;
+ // ...and pushes it onto the stack
+ l.status |= Label.PUSHED;
+ l.next = stack;
+ stack = l;
+ }
+ b = b.next;
+ }
+ }
+ this.maxStack = max;
+ } else {
+ this.maxStack = maxStack;
+ this.maxLocals = maxLocals;
+ }
+ }
+
+ public void visitEnd() {
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods: control flow analysis algorithm
+ // ------------------------------------------------------------------------
+
+ /**
+ * Computes the size of the arguments and of the return value of a method.
+ *
+ * @param desc the descriptor of a method.
+ * @return the size of the arguments of the method (plus one for the
+ * implicit this argument), argSize, and the size of its return
+ * value, retSize, packed into a single int i =
+ * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal
+ * to <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).
+ */
+ static int getArgumentsAndReturnSizes(final String desc) {
+ int n = 1;
+ int c = 1;
+ while (true) {
+ char car = desc.charAt(c++);
+ if (car == ')') {
+ car = desc.charAt(c);
+ return n << 2
+ | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1));
+ } else if (car == 'L') {
+ while (desc.charAt(c++) != ';') {
+ }
+ n += 1;
+ } else if (car == '[') {
+ while ((car = desc.charAt(c)) == '[') {
+ ++c;
+ }
+ if (car == 'D' || car == 'J') {
+ n -= 1;
+ }
+ } else if (car == 'D' || car == 'J') {
+ n += 2;
+ } else {
+ n += 1;
+ }
+ }
+ }
+
+ /**
+ * Adds a successor to the {@link #currentBlock currentBlock} block.
+ *
+ * @param info information about the control flow edge to be added.
+ * @param successor the successor block to be added to the current block.
+ */
+ private void addSuccessor(final int info, final Label successor) {
+ // creates and initializes an Edge object...
+ Edge b = new Edge();
+ b.info = info;
+ b.successor = successor;
+ // ...and adds it to the successor list of the currentBlock block
+ b.next = currentBlock.successors;
+ currentBlock.successors = b;
+ }
+
+ /**
+ * Ends the current basic block. This method must be used in the case where
+ * the current basic block does not have any successor.
+ */
+ private void noSuccessor() {
+ if (compute == FRAMES) {
+ Label l = new Label();
+ l.frame = new Frame();
+ l.frame.owner = l;
+ l.resolve(this, code.length, code.data);
+ previousBlock.successor = l;
+ previousBlock = l;
+ } else {
+ currentBlock.outputStackMax = maxStackSize;
+ }
+ currentBlock = null;
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods: stack map frames
+ // ------------------------------------------------------------------------
+
+ /**
+ * Visits a frame that has been computed from scratch.
+ *
+ * @param f the frame that must be visited.
+ */
+ private void visitFrame(final Frame f) {
+ int i, t;
+ int nTop = 0;
+ int nLocal = 0;
+ int nStack = 0;
+ int[] locals = f.inputLocals;
+ int[] stacks = f.inputStack;
+ // computes the number of locals (ignores TOP types that are just after
+ // a LONG or a DOUBLE, and all trailing TOP types)
+ for (i = 0; i < locals.length; ++i) {
+ t = locals[i];
+ if (t == Frame.TOP) {
+ ++nTop;
+ } else {
+ nLocal += nTop + 1;
+ nTop = 0;
+ }
+ if (t == Frame.LONG || t == Frame.DOUBLE) {
+ ++i;
+ }
+ }
+ // computes the stack size (ignores TOP types that are just after
+ // a LONG or a DOUBLE)
+ for (i = 0; i < stacks.length; ++i) {
+ t = stacks[i];
+ ++nStack;
+ if (t == Frame.LONG || t == Frame.DOUBLE) {
+ ++i;
+ }
+ }
+ // visits the frame and its content
+ startFrame(f.owner.position, nLocal, nStack);
+ for (i = 0; nLocal > 0; ++i, --nLocal) {
+ t = locals[i];
+ frame[frameIndex++] = t;
+ if (t == Frame.LONG || t == Frame.DOUBLE) {
+ ++i;
+ }
+ }
+ for (i = 0; i < stacks.length; ++i) {
+ t = stacks[i];
+ frame[frameIndex++] = t;
+ if (t == Frame.LONG || t == Frame.DOUBLE) {
+ ++i;
+ }
+ }
+ endFrame();
+ }
+
+ /**
+ * Starts the visit of a stack map frame.
+ *
+ * @param offset the offset of the instruction to which the frame
+ * corresponds.
+ * @param nLocal the number of local variables in the frame.
+ * @param nStack the number of stack elements in the frame.
+ */
+ private void startFrame(final int offset, final int nLocal, final int nStack)
+ {
+ int n = 3 + nLocal + nStack;
+ if (frame == null || frame.length < n) {
+ frame = new int[n];
+ }
+ frame[0] = offset;
+ frame[1] = nLocal;
+ frame[2] = nStack;
+ frameIndex = 3;
+ }
+
+ /**
+ * Checks if the visit of the current frame {@link #frame} is finished, and
+ * if yes, write it in the StackMapTable attribute.
+ */
+ private void endFrame() {
+ if (previousFrame != null) { // do not write the first frame
+ if (stackMap == null) {
+ stackMap = new ByteVector();
+ }
+ writeFrame();
+ ++frameCount;
+ }
+ previousFrame = frame;
+ frame = null;
+ }
+
+ /**
+ * Compress and writes the current frame {@link #frame} in the StackMapTable
+ * attribute.
+ */
+ private void writeFrame() {
+ int clocalsSize = frame[1];
+ int cstackSize = frame[2];
+ if ((cw.version & 0xFFFF) < Opcodes.V1_6) {
+ stackMap.putShort(frame[0]).putShort(clocalsSize);
+ writeFrameTypes(3, 3 + clocalsSize);
+ stackMap.putShort(cstackSize);
+ writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);
+ return;
+ }
+ int localsSize = previousFrame[1];
+ int type = FULL_FRAME;
+ int k = 0;
+ int delta;
+ if (frameCount == 0) {
+ delta = frame[0];
+ } else {
+ delta = frame[0] - previousFrame[0] - 1;
+ }
+ if (cstackSize == 0) {
+ k = clocalsSize - localsSize;
+ switch (k) {
+ case -3:
+ case -2:
+ case -1:
+ type = CHOP_FRAME;
+ localsSize = clocalsSize;
+ break;
+ case 0:
+ type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED;
+ break;
+ case 1:
+ case 2:
+ case 3:
+ type = APPEND_FRAME;
+ break;
+ }
+ } else if (clocalsSize == localsSize && cstackSize == 1) {
+ type = delta < 63
+ ? SAME_LOCALS_1_STACK_ITEM_FRAME
+ : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
+ }
+ if (type != FULL_FRAME) {
+ // verify if locals are the same
+ int l = 3;
+ for (int j = 0; j < localsSize; j++) {
+ if (frame[l] != previousFrame[l]) {
+ type = FULL_FRAME;
+ break;
+ }
+ l++;
+ }
+ }
+ switch (type) {
+ case SAME_FRAME:
+ stackMap.putByte(delta);
+ break;
+ case SAME_LOCALS_1_STACK_ITEM_FRAME:
+ stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
+ writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
+ break;
+ case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
+ stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
+ .putShort(delta);
+ writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
+ break;
+ case SAME_FRAME_EXTENDED:
+ stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
+ break;
+ case CHOP_FRAME:
+ stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
+ break;
+ case APPEND_FRAME:
+ stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
+ writeFrameTypes(3 + localsSize, 3 + clocalsSize);
+ break;
+ // case FULL_FRAME:
+ default:
+ stackMap.putByte(FULL_FRAME)
+ .putShort(delta)
+ .putShort(clocalsSize);
+ writeFrameTypes(3, 3 + clocalsSize);
+ stackMap.putShort(cstackSize);
+ writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);
+ }
+ }
+
+ /**
+ * Writes some types of the current frame {@link #frame} into the
+ * StackMapTableAttribute. This method converts types from the format used
+ * in {@link Label} to the format used in StackMapTable attributes. In
+ * particular, it converts type table indexes to constant pool indexes.
+ *
+ * @param start index of the first type in {@link #frame} to write.
+ * @param end index of last type in {@link #frame} to write (exclusive).
+ */
+ private void writeFrameTypes(final int start, final int end) {
+ for (int i = start; i < end; ++i) {
+ int t = frame[i];
+ int d = t & Frame.DIM;
+ if (d == 0) {
+ int v = t & Frame.BASE_VALUE;
+ switch (t & Frame.BASE_KIND) {
+ case Frame.OBJECT:
+ stackMap.putByte(7)
+ .putShort(cw.newClass(cw.typeTable[v].strVal1));
+ break;
+ case Frame.UNINITIALIZED:
+ stackMap.putByte(8).putShort(cw.typeTable[v].intVal);
+ break;
+ default:
+ stackMap.putByte(v);
+ }
+ } else {
+ StringBuffer buf = new StringBuffer();
+ d >>= 28;
+ while (d-- > 0) {
+ buf.append('[');
+ }
+ if ((t & Frame.BASE_KIND) == Frame.OBJECT) {
+ buf.append('L');
+ buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1);
+ buf.append(';');
+ } else {
+ switch (t & 0xF) {
+ case 1:
+ buf.append('I');
+ break;
+ case 2:
+ buf.append('F');
+ break;
+ case 3:
+ buf.append('D');
+ break;
+ case 9:
+ buf.append('Z');
+ break;
+ case 10:
+ buf.append('B');
+ break;
+ case 11:
+ buf.append('C');
+ break;
+ case 12:
+ buf.append('S');
+ break;
+ default:
+ buf.append('J');
+ }
+ }
+ stackMap.putByte(7).putShort(cw.newClass(buf.toString()));
+ }
+ }
+ }
+
+ private void writeFrameType(final Object type) {
+ if (type instanceof String) {
+ stackMap.putByte(7).putShort(cw.newClass((String) type));
+ } else if (type instanceof Integer) {
+ stackMap.putByte(((Integer) type).intValue());
+ } else {
+ stackMap.putByte(8).putShort(((Label) type).position);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods: dump bytecode array
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the size of the bytecode of this method.
+ *
+ * @return the size of the bytecode of this method.
+ */
+ final int getSize() {
+ if (classReaderOffset != 0) {
+ return 6 + classReaderLength;
+ }
+ if (resize) {
+ // replaces the temporary jump opcodes introduced by Label.resolve.
+ if (ClassReader.RESIZE) {
+ resizeInstructions();
+ } else {
+ throw new RuntimeException("Method code too large!");
+ }
+ }
+ int size = 8;
+ if (code.length > 0) {
+ cw.newUTF8("Code");
+ size += 18 + code.length + 8 * handlerCount;
+ if (localVar != null) {
+ cw.newUTF8("LocalVariableTable");
+ size += 8 + localVar.length;
+ }
+ if (localVarType != null) {
+ cw.newUTF8("LocalVariableTypeTable");
+ size += 8 + localVarType.length;
+ }
+ if (lineNumber != null) {
+ cw.newUTF8("LineNumberTable");
+ size += 8 + lineNumber.length;
+ }
+ if (stackMap != null) {
+ boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
+ cw.newUTF8(zip ? "StackMapTable" : "StackMap");
+ size += 8 + stackMap.length;
+ }
+ if (cattrs != null) {
+ size += cattrs.getSize(cw,
+ code.data,
+ code.length,
+ maxStack,
+ maxLocals);
+ }
+ }
+ if (exceptionCount > 0) {
+ cw.newUTF8("Exceptions");
+ size += 8 + 2 * exceptionCount;
+ }
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ cw.newUTF8("Synthetic");
+ size += 6;
+ }
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ cw.newUTF8("Deprecated");
+ size += 6;
+ }
+ if (ClassReader.SIGNATURES && signature != null) {
+ cw.newUTF8("Signature");
+ cw.newUTF8(signature);
+ size += 8;
+ }
+ if (ClassReader.ANNOTATIONS && annd != null) {
+ cw.newUTF8("AnnotationDefault");
+ size += 6 + annd.length;
+ }
+ if (ClassReader.ANNOTATIONS && anns != null) {
+ cw.newUTF8("RuntimeVisibleAnnotations");
+ size += 8 + anns.getSize();
+ }
+ if (ClassReader.ANNOTATIONS && ianns != null) {
+ cw.newUTF8("RuntimeInvisibleAnnotations");
+ size += 8 + ianns.getSize();
+ }
+ if (ClassReader.ANNOTATIONS && panns != null) {
+ cw.newUTF8("RuntimeVisibleParameterAnnotations");
+ size += 7 + 2 * (panns.length - synthetics);
+ for (int i = panns.length - 1; i >= synthetics; --i) {
+ size += panns[i] == null ? 0 : panns[i].getSize();
+ }
+ }
+ if (ClassReader.ANNOTATIONS && ipanns != null) {
+ cw.newUTF8("RuntimeInvisibleParameterAnnotations");
+ size += 7 + 2 * (ipanns.length - synthetics);
+ for (int i = ipanns.length - 1; i >= synthetics; --i) {
+ size += ipanns[i] == null ? 0 : ipanns[i].getSize();
+ }
+ }
+ if (attrs != null) {
+ size += attrs.getSize(cw, null, 0, -1, -1);
+ }
+ return size;
+ }
+
+ /**
+ * Puts the bytecode of this method in the given byte vector.
+ *
+ * @param out the byte vector into which the bytecode of this method must be
+ * copied.
+ */
+ final void put(final ByteVector out) {
+ out.putShort(access).putShort(name).putShort(desc);
+ if (classReaderOffset != 0) {
+ out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength);
+ return;
+ }
+ int attributeCount = 0;
+ if (code.length > 0) {
+ ++attributeCount;
+ }
+ if (exceptionCount > 0) {
+ ++attributeCount;
+ }
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ ++attributeCount;
+ }
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ ++attributeCount;
+ }
+ if (ClassReader.SIGNATURES && signature != null) {
+ ++attributeCount;
+ }
+ if (ClassReader.ANNOTATIONS && annd != null) {
+ ++attributeCount;
+ }
+ if (ClassReader.ANNOTATIONS && anns != null) {
+ ++attributeCount;
+ }
+ if (ClassReader.ANNOTATIONS && ianns != null) {
+ ++attributeCount;
+ }
+ if (ClassReader.ANNOTATIONS && panns != null) {
+ ++attributeCount;
+ }
+ if (ClassReader.ANNOTATIONS && ipanns != null) {
+ ++attributeCount;
+ }
+ if (attrs != null) {
+ attributeCount += attrs.getCount();
+ }
+ out.putShort(attributeCount);
+ if (code.length > 0) {
+ int size = 12 + code.length + 8 * handlerCount;
+ if (localVar != null) {
+ size += 8 + localVar.length;
+ }
+ if (localVarType != null) {
+ size += 8 + localVarType.length;
+ }
+ if (lineNumber != null) {
+ size += 8 + lineNumber.length;
+ }
+ if (stackMap != null) {
+ size += 8 + stackMap.length;
+ }
+ if (cattrs != null) {
+ size += cattrs.getSize(cw,
+ code.data,
+ code.length,
+ maxStack,
+ maxLocals);
+ }
+ out.putShort(cw.newUTF8("Code")).putInt(size);
+ out.putShort(maxStack).putShort(maxLocals);
+ out.putInt(code.length).putByteArray(code.data, 0, code.length);
+ out.putShort(handlerCount);
+ if (handlerCount > 0) {
+ Handler h = firstHandler;
+ while (h != null) {
+ out.putShort(h.start.position)
+ .putShort(h.end.position)
+ .putShort(h.handler.position)
+ .putShort(h.type);
+ h = h.next;
+ }
+ }
+ attributeCount = 0;
+ if (localVar != null) {
+ ++attributeCount;
+ }
+ if (localVarType != null) {
+ ++attributeCount;
+ }
+ if (lineNumber != null) {
+ ++attributeCount;
+ }
+ if (stackMap != null) {
+ ++attributeCount;
+ }
+ if (cattrs != null) {
+ attributeCount += cattrs.getCount();
+ }
+ out.putShort(attributeCount);
+ if (localVar != null) {
+ out.putShort(cw.newUTF8("LocalVariableTable"));
+ out.putInt(localVar.length + 2).putShort(localVarCount);
+ out.putByteArray(localVar.data, 0, localVar.length);
+ }
+ if (localVarType != null) {
+ out.putShort(cw.newUTF8("LocalVariableTypeTable"));
+ out.putInt(localVarType.length + 2).putShort(localVarTypeCount);
+ out.putByteArray(localVarType.data, 0, localVarType.length);
+ }
+ if (lineNumber != null) {
+ out.putShort(cw.newUTF8("LineNumberTable"));
+ out.putInt(lineNumber.length + 2).putShort(lineNumberCount);
+ out.putByteArray(lineNumber.data, 0, lineNumber.length);
+ }
+ if (stackMap != null) {
+ boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
+ out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap"));
+ out.putInt(stackMap.length + 2).putShort(frameCount);
+ out.putByteArray(stackMap.data, 0, stackMap.length);
+ }
+ if (cattrs != null) {
+ cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out);
+ }
+ }
+ if (exceptionCount > 0) {
+ out.putShort(cw.newUTF8("Exceptions"))
+ .putInt(2 * exceptionCount + 2);
+ out.putShort(exceptionCount);
+ for (int i = 0; i < exceptionCount; ++i) {
+ out.putShort(exceptions[i]);
+ }
+ }
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ out.putShort(cw.newUTF8("Synthetic")).putInt(0);
+ }
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ out.putShort(cw.newUTF8("Deprecated")).putInt(0);
+ }
+ if (ClassReader.SIGNATURES && signature != null) {
+ out.putShort(cw.newUTF8("Signature"))
+ .putInt(2)
+ .putShort(cw.newUTF8(signature));
+ }
+ if (ClassReader.ANNOTATIONS && annd != null) {
+ out.putShort(cw.newUTF8("AnnotationDefault"));
+ out.putInt(annd.length);
+ out.putByteArray(annd.data, 0, annd.length);
+ }
+ if (ClassReader.ANNOTATIONS && anns != null) {
+ out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
+ anns.put(out);
+ }
+ if (ClassReader.ANNOTATIONS && ianns != null) {
+ out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
+ ianns.put(out);
+ }
+ if (ClassReader.ANNOTATIONS && panns != null) {
+ out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations"));
+ AnnotationWriter.put(panns, synthetics, out);
+ }
+ if (ClassReader.ANNOTATIONS && ipanns != null) {
+ out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations"));
+ AnnotationWriter.put(ipanns, synthetics, out);
+ }
+ if (attrs != null) {
+ attrs.put(cw, null, 0, -1, -1, out);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)
+ // ------------------------------------------------------------------------
+
+ /**
+ * Resizes and replaces the temporary instructions inserted by
+ * {@link Label#resolve} for wide forward jumps, while keeping jump offsets
+ * and instruction addresses consistent. This may require to resize other
+ * existing instructions, or even to introduce new instructions: for
+ * example, increasing the size of an instruction by 2 at the middle of a
+ * method can increases the offset of an IFEQ instruction from 32766 to
+ * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W
+ * 32765. This, in turn, may require to increase the size of another jump
+ * instruction, and so on... All these operations are handled automatically
+ * by this method. <p> <i>This method must be called after all the method
+ * that is being built has been visited</i>. In particular, the
+ * {@link Label Label} objects used to construct the method are no longer
+ * valid after this method has been called.
+ */
+ private void resizeInstructions() {
+ byte[] b = code.data; // bytecode of the method
+ int u, v, label; // indexes in b
+ int i, j; // loop indexes
+ /*
+ * 1st step: As explained above, resizing an instruction may require to
+ * resize another one, which may require to resize yet another one, and
+ * so on. The first step of the algorithm consists in finding all the
+ * instructions that need to be resized, without modifying the code.
+ * This is done by the following "fix point" algorithm:
+ *
+ * Parse the code to find the jump instructions whose offset will need
+ * more than 2 bytes to be stored (the future offset is computed from
+ * the current offset and from the number of bytes that will be inserted
+ * or removed between the source and target instructions). For each such
+ * instruction, adds an entry in (a copy of) the indexes and sizes
+ * arrays (if this has not already been done in a previous iteration!).
+ *
+ * If at least one entry has been added during the previous step, go
+ * back to the beginning, otherwise stop.
+ *
+ * In fact the real algorithm is complicated by the fact that the size
+ * of TABLESWITCH and LOOKUPSWITCH instructions depends on their
+ * position in the bytecode (because of padding). In order to ensure the
+ * convergence of the algorithm, the number of bytes to be added or
+ * removed from these instructions is over estimated during the previous
+ * loop, and computed exactly only after the loop is finished (this
+ * requires another pass to parse the bytecode of the method).
+ */
+ int[] allIndexes = new int[0]; // copy of indexes
+ int[] allSizes = new int[0]; // copy of sizes
+ boolean[] resize; // instructions to be resized
+ int newOffset; // future offset of a jump instruction
+
+ resize = new boolean[code.length];
+
+ // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
+ int state = 3;
+ do {
+ if (state == 3) {
+ state = 2;
+ }
+ u = 0;
+ while (u < b.length) {
+ int opcode = b[u] & 0xFF; // opcode of current instruction
+ int insert = 0; // bytes to be added after this instruction
+
+ switch (ClassWriter.TYPE[opcode]) {
+ case ClassWriter.NOARG_INSN:
+ case ClassWriter.IMPLVAR_INSN:
+ u += 1;
+ break;
+ case ClassWriter.LABEL_INSN:
+ if (opcode > 201) {
+ // converts temporary opcodes 202 to 217, 218 and
+ // 219 to IFEQ ... JSR (inclusive), IFNULL and
+ // IFNONNULL
+ opcode = opcode < 218 ? opcode - 49 : opcode - 20;
+ label = u + readUnsignedShort(b, u + 1);
+ } else {
+ label = u + readShort(b, u + 1);
+ }
+ newOffset = getNewOffset(allIndexes, allSizes, u, label);
+ if (newOffset < Short.MIN_VALUE
+ || newOffset > Short.MAX_VALUE)
+ {
+ if (!resize[u]) {
+ if (opcode == Opcodes.GOTO
+ || opcode == Opcodes.JSR)
+ {
+ // two additional bytes will be required to
+ // replace this GOTO or JSR instruction with
+ // a GOTO_W or a JSR_W
+ insert = 2;
+ } else {
+ // five additional bytes will be required to
+ // replace this IFxxx <l> instruction with
+ // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx
+ // is the "opposite" opcode of IFxxx (i.e.,
+ // IFNE for IFEQ) and where <l'> designates
+ // the instruction just after the GOTO_W.
+ insert = 5;
+ }
+ resize[u] = true;
+ }
+ }
+ u += 3;
+ break;
+ case ClassWriter.LABELW_INSN:
+ u += 5;
+ break;
+ case ClassWriter.TABL_INSN:
+ if (state == 1) {
+ // true number of bytes to be added (or removed)
+ // from this instruction = (future number of padding
+ // bytes - current number of padding byte) -
+ // previously over estimated variation =
+ // = ((3 - newOffset%4) - (3 - u%4)) - u%4
+ // = (-newOffset%4 + u%4) - u%4
+ // = -(newOffset & 3)
+ newOffset = getNewOffset(allIndexes, allSizes, 0, u);
+ insert = -(newOffset & 3);
+ } else if (!resize[u]) {
+ // over estimation of the number of bytes to be
+ // added to this instruction = 3 - current number
+ // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3
+ insert = u & 3;
+ resize[u] = true;
+ }
+ // skips instruction
+ u = u + 4 - (u & 3);
+ u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12;
+ break;
+ case ClassWriter.LOOK_INSN:
+ if (state == 1) {
+ // like TABL_INSN
+ newOffset = getNewOffset(allIndexes, allSizes, 0, u);
+ insert = -(newOffset & 3);
+ } else if (!resize[u]) {
+ // like TABL_INSN
+ insert = u & 3;
+ resize[u] = true;
+ }
+ // skips instruction
+ u = u + 4 - (u & 3);
+ u += 8 * readInt(b, u + 4) + 8;
+ break;
+ case ClassWriter.WIDE_INSN:
+ opcode = b[u + 1] & 0xFF;
+ if (opcode == Opcodes.IINC) {
+ u += 6;
+ } else {
+ u += 4;
+ }
+ break;
+ case ClassWriter.VAR_INSN:
+ case ClassWriter.SBYTE_INSN:
+ case ClassWriter.LDC_INSN:
+ u += 2;
+ break;
+ case ClassWriter.SHORT_INSN:
+ case ClassWriter.LDCW_INSN:
+ case ClassWriter.FIELDORMETH_INSN:
+ case ClassWriter.TYPE_INSN:
+ case ClassWriter.IINC_INSN:
+ u += 3;
+ break;
+ case ClassWriter.ITFMETH_INSN:
+ u += 5;
+ break;
+ // case ClassWriter.MANA_INSN:
+ default:
+ u += 4;
+ break;
+ }
+ if (insert != 0) {
+ // adds a new (u, insert) entry in the allIndexes and
+ // allSizes arrays
+ int[] newIndexes = new int[allIndexes.length + 1];
+ int[] newSizes = new int[allSizes.length + 1];
+ System.arraycopy(allIndexes,
+ 0,
+ newIndexes,
+ 0,
+ allIndexes.length);
+ System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length);
+ newIndexes[allIndexes.length] = u;
+ newSizes[allSizes.length] = insert;
+ allIndexes = newIndexes;
+ allSizes = newSizes;
+ if (insert > 0) {
+ state = 3;
+ }
+ }
+ }
+ if (state < 3) {
+ --state;
+ }
+ } while (state != 0);
+
+ // 2nd step:
+ // copies the bytecode of the method into a new bytevector, updates the
+ // offsets, and inserts (or removes) bytes as requested.
+
+ ByteVector newCode = new ByteVector(code.length);
+
+ u = 0;
+ while (u < code.length) {
+ int opcode = b[u] & 0xFF;
+ switch (ClassWriter.TYPE[opcode]) {
+ case ClassWriter.NOARG_INSN:
+ case ClassWriter.IMPLVAR_INSN:
+ newCode.putByte(opcode);
+ u += 1;
+ break;
+ case ClassWriter.LABEL_INSN:
+ if (opcode > 201) {
+ // changes temporary opcodes 202 to 217 (inclusive), 218
+ // and 219 to IFEQ ... JSR (inclusive), IFNULL and
+ // IFNONNULL
+ opcode = opcode < 218 ? opcode - 49 : opcode - 20;
+ label = u + readUnsignedShort(b, u + 1);
+ } else {
+ label = u + readShort(b, u + 1);
+ }
+ newOffset = getNewOffset(allIndexes, allSizes, u, label);
+ if (resize[u]) {
+ // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
+ // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is
+ // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
+ // and where <l'> designates the instruction just after
+ // the GOTO_W.
+ if (opcode == Opcodes.GOTO) {
+ newCode.putByte(200); // GOTO_W
+ } else if (opcode == Opcodes.JSR) {
+ newCode.putByte(201); // JSR_W
+ } else {
+ newCode.putByte(opcode <= 166
+ ? ((opcode + 1) ^ 1) - 1
+ : opcode ^ 1);
+ newCode.putShort(8); // jump offset
+ newCode.putByte(200); // GOTO_W
+ // newOffset now computed from start of GOTO_W
+ newOffset -= 3;
+ }
+ newCode.putInt(newOffset);
+ } else {
+ newCode.putByte(opcode);
+ newCode.putShort(newOffset);
+ }
+ u += 3;
+ break;
+ case ClassWriter.LABELW_INSN:
+ label = u + readInt(b, u + 1);
+ newOffset = getNewOffset(allIndexes, allSizes, u, label);
+ newCode.putByte(opcode);
+ newCode.putInt(newOffset);
+ u += 5;
+ break;
+ case ClassWriter.TABL_INSN:
+ // skips 0 to 3 padding bytes
+ v = u;
+ u = u + 4 - (v & 3);
+ // reads and copies instruction
+ newCode.putByte(Opcodes.TABLESWITCH);
+ newCode.length += (4 - newCode.length % 4) % 4;
+ label = v + readInt(b, u);
+ u += 4;
+ newOffset = getNewOffset(allIndexes, allSizes, v, label);
+ newCode.putInt(newOffset);
+ j = readInt(b, u);
+ u += 4;
+ newCode.putInt(j);
+ j = readInt(b, u) - j + 1;
+ u += 4;
+ newCode.putInt(readInt(b, u - 4));
+ for (; j > 0; --j) {
+ label = v + readInt(b, u);
+ u += 4;
+ newOffset = getNewOffset(allIndexes, allSizes, v, label);
+ newCode.putInt(newOffset);
+ }
+ break;
+ case ClassWriter.LOOK_INSN:
+ // skips 0 to 3 padding bytes
+ v = u;
+ u = u + 4 - (v & 3);
+ // reads and copies instruction
+ newCode.putByte(Opcodes.LOOKUPSWITCH);
+ newCode.length += (4 - newCode.length % 4) % 4;
+ label = v + readInt(b, u);
+ u += 4;
+ newOffset = getNewOffset(allIndexes, allSizes, v, label);
+ newCode.putInt(newOffset);
+ j = readInt(b, u);
+ u += 4;
+ newCode.putInt(j);
+ for (; j > 0; --j) {
+ newCode.putInt(readInt(b, u));
+ u += 4;
+ label = v + readInt(b, u);
+ u += 4;
+ newOffset = getNewOffset(allIndexes, allSizes, v, label);
+ newCode.putInt(newOffset);
+ }
+ break;
+ case ClassWriter.WIDE_INSN:
+ opcode = b[u + 1] & 0xFF;
+ if (opcode == Opcodes.IINC) {
+ newCode.putByteArray(b, u, 6);
+ u += 6;
+ } else {
+ newCode.putByteArray(b, u, 4);
+ u += 4;
+ }
+ break;
+ case ClassWriter.VAR_INSN:
+ case ClassWriter.SBYTE_INSN:
+ case ClassWriter.LDC_INSN:
+ newCode.putByteArray(b, u, 2);
+ u += 2;
+ break;
+ case ClassWriter.SHORT_INSN:
+ case ClassWriter.LDCW_INSN:
+ case ClassWriter.FIELDORMETH_INSN:
+ case ClassWriter.TYPE_INSN:
+ case ClassWriter.IINC_INSN:
+ newCode.putByteArray(b, u, 3);
+ u += 3;
+ break;
+ case ClassWriter.ITFMETH_INSN:
+ newCode.putByteArray(b, u, 5);
+ u += 5;
+ break;
+ // case MANA_INSN:
+ default:
+ newCode.putByteArray(b, u, 4);
+ u += 4;
+ break;
+ }
+ }
+
+ // recomputes the stack map frames
+ if (frameCount > 0) {
+ if (compute == FRAMES) {
+ frameCount = 0;
+ stackMap = null;
+ previousFrame = null;
+ frame = null;
+ Frame f = new Frame();
+ f.owner = labels;
+ Type[] args = Type.getArgumentTypes(descriptor);
+ f.initInputFrame(cw, access, args, maxLocals);
+ visitFrame(f);
+ Label l = labels;
+ while (l != null) {
+ /*
+ * here we need the original label position. getNewOffset
+ * must therefore never have been called for this label.
+ */
+ u = l.position - 3;
+ if ((l.status & Label.STORE) != 0 || (u >= 0 && resize[u]))
+ {
+ getNewOffset(allIndexes, allSizes, l);
+ // TODO update offsets in UNINITIALIZED values
+ visitFrame(l.frame);
+ }
+ l = l.successor;
+ }
+ } else {
+ /*
+ * Resizing an existing stack map frame table is really hard.
+ * Not only the table must be parsed to update the offets, but
+ * new frames may be needed for jump instructions that were
+ * inserted by this method. And updating the offsets or
+ * inserting frames can change the format of the following
+ * frames, in case of packed frames. In practice the whole table
+ * must be recomputed. For this the frames are marked as
+ * potentially invalid. This will cause the whole class to be
+ * reread and rewritten with the COMPUTE_FRAMES option (see the
+ * ClassWriter.toByteArray method). This is not very efficient
+ * but is much easier and requires much less code than any other
+ * method I can think of.
+ */
+ cw.invalidFrames = true;
+ }
+ }
+ // updates the exception handler block labels
+ Handler h = firstHandler;
+ while (h != null) {
+ getNewOffset(allIndexes, allSizes, h.start);
+ getNewOffset(allIndexes, allSizes, h.end);
+ getNewOffset(allIndexes, allSizes, h.handler);
+ h = h.next;
+ }
+ // updates the instructions addresses in the
+ // local var and line number tables
+ for (i = 0; i < 2; ++i) {
+ ByteVector bv = i == 0 ? localVar : localVarType;
+ if (bv != null) {
+ b = bv.data;
+ u = 0;
+ while (u < bv.length) {
+ label = readUnsignedShort(b, u);
+ newOffset = getNewOffset(allIndexes, allSizes, 0, label);
+ writeShort(b, u, newOffset);
+ label += readUnsignedShort(b, u + 2);
+ newOffset = getNewOffset(allIndexes, allSizes, 0, label)
+ - newOffset;
+ writeShort(b, u + 2, newOffset);
+ u += 10;
+ }
+ }
+ }
+ if (lineNumber != null) {
+ b = lineNumber.data;
+ u = 0;
+ while (u < lineNumber.length) {
+ writeShort(b, u, getNewOffset(allIndexes,
+ allSizes,
+ 0,
+ readUnsignedShort(b, u)));
+ u += 4;
+ }
+ }
+ // updates the labels of the other attributes
+ Attribute attr = cattrs;
+ while (attr != null) {
+ Label[] labels = attr.getLabels();
+ if (labels != null) {
+ for (i = labels.length - 1; i >= 0; --i) {
+ getNewOffset(allIndexes, allSizes, labels[i]);
+ }
+ }
+ attr = attr.next;
+ }
+
+ // replaces old bytecodes with new ones
+ code = newCode;
+ }
+
+ /**
+ * Reads an unsigned short value in the given byte array.
+ *
+ * @param b a byte array.
+ * @param index the start index of the value to be read.
+ * @return the read value.
+ */
+ static int readUnsignedShort(final byte[] b, final int index) {
+ return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
+ }
+
+ /**
+ * Reads a signed short value in the given byte array.
+ *
+ * @param b a byte array.
+ * @param index the start index of the value to be read.
+ * @return the read value.
+ */
+ static short readShort(final byte[] b, final int index) {
+ return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
+ }
+
+ /**
+ * Reads a signed int value in the given byte array.
+ *
+ * @param b a byte array.
+ * @param index the start index of the value to be read.
+ * @return the read value.
+ */
+ static int readInt(final byte[] b, final int index) {
+ return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
+ | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
+ }
+
+ /**
+ * Writes a short value in the given byte array.
+ *
+ * @param b a byte array.
+ * @param index where the first byte of the short value must be written.
+ * @param s the value to be written in the given byte array.
+ */
+ static void writeShort(final byte[] b, final int index, final int s) {
+ b[index] = (byte) (s >>> 8);
+ b[index + 1] = (byte) s;
+ }
+
+ /**
+ * Computes the future value of a bytecode offset. <p> Note: it is possible
+ * to have several entries for the same instruction in the <tt>indexes</tt>
+ * and <tt>sizes</tt>: two entries (index=a,size=b) and (index=a,size=b')
+ * are equivalent to a single entry (index=a,size=b+b').
+ *
+ * @param indexes current positions of the instructions to be resized. Each
+ * instruction must be designated by the index of its <i>last</i>
+ * byte, plus one (or, in other words, by the index of the <i>first</i>
+ * byte of the <i>next</i> instruction).
+ * @param sizes the number of bytes to be <i>added</i> to the above
+ * instructions. More precisely, for each i < <tt>len</tt>,
+ * <tt>sizes</tt>[i] bytes will be added at the end of the
+ * instruction designated by <tt>indexes</tt>[i] or, if
+ * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
+ * bytes of the instruction will be removed (the instruction size
+ * <i>must not</i> become negative or null).
+ * @param begin index of the first byte of the source instruction.
+ * @param end index of the first byte of the target instruction.
+ * @return the future value of the given bytecode offset.
+ */
+ static int getNewOffset(
+ final int[] indexes,
+ final int[] sizes,
+ final int begin,
+ final int end)
+ {
+ int offset = end - begin;
+ for (int i = 0; i < indexes.length; ++i) {
+ if (begin < indexes[i] && indexes[i] <= end) {
+ // forward jump
+ offset += sizes[i];
+ } else if (end < indexes[i] && indexes[i] <= begin) {
+ // backward jump
+ offset -= sizes[i];
+ }
+ }
+ return offset;
+ }
+
+ /**
+ * Updates the offset of the given label.
+ *
+ * @param indexes current positions of the instructions to be resized. Each
+ * instruction must be designated by the index of its <i>last</i>
+ * byte, plus one (or, in other words, by the index of the <i>first</i>
+ * byte of the <i>next</i> instruction).
+ * @param sizes the number of bytes to be <i>added</i> to the above
+ * instructions. More precisely, for each i < <tt>len</tt>,
+ * <tt>sizes</tt>[i] bytes will be added at the end of the
+ * instruction designated by <tt>indexes</tt>[i] or, if
+ * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
+ * bytes of the instruction will be removed (the instruction size
+ * <i>must not</i> become negative or null).
+ * @param label the label whose offset must be updated.
+ */
+ static void getNewOffset(
+ final int[] indexes,
+ final int[] sizes,
+ final Label label)
+ {
+ if ((label.status & Label.RESIZED) == 0) {
+ label.position = getNewOffset(indexes, sizes, 0, label.position);
+ label.status |= Label.RESIZED;
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/Opcodes.java b/cglib-and-asm/src/org/mockito/asm/Opcodes.java
new file mode 100644
index 0000000..0dce3bf
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/Opcodes.java
@@ -0,0 +1,341 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+/**
+ * Defines the JVM opcodes, access flags and array type codes. This interface
+ * does not define all the JVM opcodes because some opcodes are automatically
+ * handled. For example, the xLOAD and xSTORE opcodes are automatically replaced
+ * by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n
+ * opcodes are therefore not defined in this interface. Likewise for LDC,
+ * automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and
+ * JSR_W.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public interface Opcodes {
+
+ // versions
+
+ int V1_1 = 3 << 16 | 45;
+ int V1_2 = 0 << 16 | 46;
+ int V1_3 = 0 << 16 | 47;
+ int V1_4 = 0 << 16 | 48;
+ int V1_5 = 0 << 16 | 49;
+ int V1_6 = 0 << 16 | 50;
+
+ // access flags
+
+ int ACC_PUBLIC = 0x0001; // class, field, method
+ int ACC_PRIVATE = 0x0002; // class, field, method
+ int ACC_PROTECTED = 0x0004; // class, field, method
+ int ACC_STATIC = 0x0008; // field, method
+ int ACC_FINAL = 0x0010; // class, field, method
+ int ACC_SUPER = 0x0020; // class
+ int ACC_SYNCHRONIZED = 0x0020; // method
+ int ACC_VOLATILE = 0x0040; // field
+ int ACC_BRIDGE = 0x0040; // method
+ int ACC_VARARGS = 0x0080; // method
+ int ACC_TRANSIENT = 0x0080; // field
+ int ACC_NATIVE = 0x0100; // method
+ int ACC_INTERFACE = 0x0200; // class
+ int ACC_ABSTRACT = 0x0400; // class, method
+ int ACC_STRICT = 0x0800; // method
+ int ACC_SYNTHETIC = 0x1000; // class, field, method
+ int ACC_ANNOTATION = 0x2000; // class
+ int ACC_ENUM = 0x4000; // class(?) field inner
+
+ // ASM specific pseudo access flags
+
+ int ACC_DEPRECATED = 131072; // class, field, method
+
+ // types for NEWARRAY
+
+ int T_BOOLEAN = 4;
+ int T_CHAR = 5;
+ int T_FLOAT = 6;
+ int T_DOUBLE = 7;
+ int T_BYTE = 8;
+ int T_SHORT = 9;
+ int T_INT = 10;
+ int T_LONG = 11;
+
+ // stack map frame types
+
+ /**
+ * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}.
+ */
+ int F_NEW = -1;
+
+ /**
+ * Represents a compressed frame with complete frame data.
+ */
+ int F_FULL = 0;
+
+ /**
+ * Represents a compressed frame where locals are the same as the locals in
+ * the previous frame, except that additional 1-3 locals are defined, and
+ * with an empty stack.
+ */
+ int F_APPEND = 1;
+
+ /**
+ * Represents a compressed frame where locals are the same as the locals in
+ * the previous frame, except that the last 1-3 locals are absent and with
+ * an empty stack.
+ */
+ int F_CHOP = 2;
+
+ /**
+ * Represents a compressed frame with exactly the same locals as the
+ * previous frame and with an empty stack.
+ */
+ int F_SAME = 3;
+
+ /**
+ * Represents a compressed frame with exactly the same locals as the
+ * previous frame and with a single value on the stack.
+ */
+ int F_SAME1 = 4;
+
+ Integer TOP = new Integer(0);
+ Integer INTEGER = new Integer(1);
+ Integer FLOAT = new Integer(2);
+ Integer DOUBLE = new Integer(3);
+ Integer LONG = new Integer(4);
+ Integer NULL = new Integer(5);
+ Integer UNINITIALIZED_THIS = new Integer(6);
+
+ // opcodes // visit method (- = idem)
+
+ int NOP = 0; // visitInsn
+ int ACONST_NULL = 1; // -
+ int ICONST_M1 = 2; // -
+ int ICONST_0 = 3; // -
+ int ICONST_1 = 4; // -
+ int ICONST_2 = 5; // -
+ int ICONST_3 = 6; // -
+ int ICONST_4 = 7; // -
+ int ICONST_5 = 8; // -
+ int LCONST_0 = 9; // -
+ int LCONST_1 = 10; // -
+ int FCONST_0 = 11; // -
+ int FCONST_1 = 12; // -
+ int FCONST_2 = 13; // -
+ int DCONST_0 = 14; // -
+ int DCONST_1 = 15; // -
+ int BIPUSH = 16; // visitIntInsn
+ int SIPUSH = 17; // -
+ int LDC = 18; // visitLdcInsn
+ // int LDC_W = 19; // -
+ // int LDC2_W = 20; // -
+ int ILOAD = 21; // visitVarInsn
+ int LLOAD = 22; // -
+ int FLOAD = 23; // -
+ int DLOAD = 24; // -
+ int ALOAD = 25; // -
+ // int ILOAD_0 = 26; // -
+ // int ILOAD_1 = 27; // -
+ // int ILOAD_2 = 28; // -
+ // int ILOAD_3 = 29; // -
+ // int LLOAD_0 = 30; // -
+ // int LLOAD_1 = 31; // -
+ // int LLOAD_2 = 32; // -
+ // int LLOAD_3 = 33; // -
+ // int FLOAD_0 = 34; // -
+ // int FLOAD_1 = 35; // -
+ // int FLOAD_2 = 36; // -
+ // int FLOAD_3 = 37; // -
+ // int DLOAD_0 = 38; // -
+ // int DLOAD_1 = 39; // -
+ // int DLOAD_2 = 40; // -
+ // int DLOAD_3 = 41; // -
+ // int ALOAD_0 = 42; // -
+ // int ALOAD_1 = 43; // -
+ // int ALOAD_2 = 44; // -
+ // int ALOAD_3 = 45; // -
+ int IALOAD = 46; // visitInsn
+ int LALOAD = 47; // -
+ int FALOAD = 48; // -
+ int DALOAD = 49; // -
+ int AALOAD = 50; // -
+ int BALOAD = 51; // -
+ int CALOAD = 52; // -
+ int SALOAD = 53; // -
+ int ISTORE = 54; // visitVarInsn
+ int LSTORE = 55; // -
+ int FSTORE = 56; // -
+ int DSTORE = 57; // -
+ int ASTORE = 58; // -
+ // int ISTORE_0 = 59; // -
+ // int ISTORE_1 = 60; // -
+ // int ISTORE_2 = 61; // -
+ // int ISTORE_3 = 62; // -
+ // int LSTORE_0 = 63; // -
+ // int LSTORE_1 = 64; // -
+ // int LSTORE_2 = 65; // -
+ // int LSTORE_3 = 66; // -
+ // int FSTORE_0 = 67; // -
+ // int FSTORE_1 = 68; // -
+ // int FSTORE_2 = 69; // -
+ // int FSTORE_3 = 70; // -
+ // int DSTORE_0 = 71; // -
+ // int DSTORE_1 = 72; // -
+ // int DSTORE_2 = 73; // -
+ // int DSTORE_3 = 74; // -
+ // int ASTORE_0 = 75; // -
+ // int ASTORE_1 = 76; // -
+ // int ASTORE_2 = 77; // -
+ // int ASTORE_3 = 78; // -
+ int IASTORE = 79; // visitInsn
+ int LASTORE = 80; // -
+ int FASTORE = 81; // -
+ int DASTORE = 82; // -
+ int AASTORE = 83; // -
+ int BASTORE = 84; // -
+ int CASTORE = 85; // -
+ int SASTORE = 86; // -
+ int POP = 87; // -
+ int POP2 = 88; // -
+ int DUP = 89; // -
+ int DUP_X1 = 90; // -
+ int DUP_X2 = 91; // -
+ int DUP2 = 92; // -
+ int DUP2_X1 = 93; // -
+ int DUP2_X2 = 94; // -
+ int SWAP = 95; // -
+ int IADD = 96; // -
+ int LADD = 97; // -
+ int FADD = 98; // -
+ int DADD = 99; // -
+ int ISUB = 100; // -
+ int LSUB = 101; // -
+ int FSUB = 102; // -
+ int DSUB = 103; // -
+ int IMUL = 104; // -
+ int LMUL = 105; // -
+ int FMUL = 106; // -
+ int DMUL = 107; // -
+ int IDIV = 108; // -
+ int LDIV = 109; // -
+ int FDIV = 110; // -
+ int DDIV = 111; // -
+ int IREM = 112; // -
+ int LREM = 113; // -
+ int FREM = 114; // -
+ int DREM = 115; // -
+ int INEG = 116; // -
+ int LNEG = 117; // -
+ int FNEG = 118; // -
+ int DNEG = 119; // -
+ int ISHL = 120; // -
+ int LSHL = 121; // -
+ int ISHR = 122; // -
+ int LSHR = 123; // -
+ int IUSHR = 124; // -
+ int LUSHR = 125; // -
+ int IAND = 126; // -
+ int LAND = 127; // -
+ int IOR = 128; // -
+ int LOR = 129; // -
+ int IXOR = 130; // -
+ int LXOR = 131; // -
+ int IINC = 132; // visitIincInsn
+ int I2L = 133; // visitInsn
+ int I2F = 134; // -
+ int I2D = 135; // -
+ int L2I = 136; // -
+ int L2F = 137; // -
+ int L2D = 138; // -
+ int F2I = 139; // -
+ int F2L = 140; // -
+ int F2D = 141; // -
+ int D2I = 142; // -
+ int D2L = 143; // -
+ int D2F = 144; // -
+ int I2B = 145; // -
+ int I2C = 146; // -
+ int I2S = 147; // -
+ int LCMP = 148; // -
+ int FCMPL = 149; // -
+ int FCMPG = 150; // -
+ int DCMPL = 151; // -
+ int DCMPG = 152; // -
+ int IFEQ = 153; // visitJumpInsn
+ int IFNE = 154; // -
+ int IFLT = 155; // -
+ int IFGE = 156; // -
+ int IFGT = 157; // -
+ int IFLE = 158; // -
+ int IF_ICMPEQ = 159; // -
+ int IF_ICMPNE = 160; // -
+ int IF_ICMPLT = 161; // -
+ int IF_ICMPGE = 162; // -
+ int IF_ICMPGT = 163; // -
+ int IF_ICMPLE = 164; // -
+ int IF_ACMPEQ = 165; // -
+ int IF_ACMPNE = 166; // -
+ int GOTO = 167; // -
+ int JSR = 168; // -
+ int RET = 169; // visitVarInsn
+ int TABLESWITCH = 170; // visiTableSwitchInsn
+ int LOOKUPSWITCH = 171; // visitLookupSwitch
+ int IRETURN = 172; // visitInsn
+ int LRETURN = 173; // -
+ int FRETURN = 174; // -
+ int DRETURN = 175; // -
+ int ARETURN = 176; // -
+ int RETURN = 177; // -
+ int GETSTATIC = 178; // visitFieldInsn
+ int PUTSTATIC = 179; // -
+ int GETFIELD = 180; // -
+ int PUTFIELD = 181; // -
+ int INVOKEVIRTUAL = 182; // visitMethodInsn
+ int INVOKESPECIAL = 183; // -
+ int INVOKESTATIC = 184; // -
+ int INVOKEINTERFACE = 185; // -
+ // int UNUSED = 186; // NOT VISITED
+ int NEW = 187; // visitTypeInsn
+ int NEWARRAY = 188; // visitIntInsn
+ int ANEWARRAY = 189; // visitTypeInsn
+ int ARRAYLENGTH = 190; // visitInsn
+ int ATHROW = 191; // -
+ int CHECKCAST = 192; // visitTypeInsn
+ int INSTANCEOF = 193; // -
+ int MONITORENTER = 194; // visitInsn
+ int MONITOREXIT = 195; // -
+ // int WIDE = 196; // NOT VISITED
+ int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn
+ int IFNULL = 198; // visitJumpInsn
+ int IFNONNULL = 199; // -
+ // int GOTO_W = 200; // -
+ // int JSR_W = 201; // -
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/Type.java b/cglib-and-asm/src/org/mockito/asm/Type.java
new file mode 100644
index 0000000..5ab06b4
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/Type.java
@@ -0,0 +1,794 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * A Java type. This class can be used to make it easier to manipulate type and
+ * method descriptors.
+ *
+ * @author Eric Bruneton
+ * @author Chris Nokleberg
+ */
+public class Type {
+
+ /**
+ * The sort of the <tt>void</tt> type. See {@link #getSort getSort}.
+ */
+ public static final int VOID = 0;
+
+ /**
+ * The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}.
+ */
+ public static final int BOOLEAN = 1;
+
+ /**
+ * The sort of the <tt>char</tt> type. See {@link #getSort getSort}.
+ */
+ public static final int CHAR = 2;
+
+ /**
+ * The sort of the <tt>byte</tt> type. See {@link #getSort getSort}.
+ */
+ public static final int BYTE = 3;
+
+ /**
+ * The sort of the <tt>short</tt> type. See {@link #getSort getSort}.
+ */
+ public static final int SHORT = 4;
+
+ /**
+ * The sort of the <tt>int</tt> type. See {@link #getSort getSort}.
+ */
+ public static final int INT = 5;
+
+ /**
+ * The sort of the <tt>float</tt> type. See {@link #getSort getSort}.
+ */
+ public static final int FLOAT = 6;
+
+ /**
+ * The sort of the <tt>long</tt> type. See {@link #getSort getSort}.
+ */
+ public static final int LONG = 7;
+
+ /**
+ * The sort of the <tt>double</tt> type. See {@link #getSort getSort}.
+ */
+ public static final int DOUBLE = 8;
+
+ /**
+ * The sort of array reference types. See {@link #getSort getSort}.
+ */
+ public static final int ARRAY = 9;
+
+ /**
+ * The sort of object reference type. See {@link #getSort getSort}.
+ */
+ public static final int OBJECT = 10;
+
+ /**
+ * The <tt>void</tt> type.
+ */
+ public static final Type VOID_TYPE = new Type(VOID);
+
+ /**
+ * The <tt>boolean</tt> type.
+ */
+ public static final Type BOOLEAN_TYPE = new Type(BOOLEAN);
+
+ /**
+ * The <tt>char</tt> type.
+ */
+ public static final Type CHAR_TYPE = new Type(CHAR);
+
+ /**
+ * The <tt>byte</tt> type.
+ */
+ public static final Type BYTE_TYPE = new Type(BYTE);
+
+ /**
+ * The <tt>short</tt> type.
+ */
+ public static final Type SHORT_TYPE = new Type(SHORT);
+
+ /**
+ * The <tt>int</tt> type.
+ */
+ public static final Type INT_TYPE = new Type(INT);
+
+ /**
+ * The <tt>float</tt> type.
+ */
+ public static final Type FLOAT_TYPE = new Type(FLOAT);
+
+ /**
+ * The <tt>long</tt> type.
+ */
+ public static final Type LONG_TYPE = new Type(LONG);
+
+ /**
+ * The <tt>double</tt> type.
+ */
+ public static final Type DOUBLE_TYPE = new Type(DOUBLE);
+
+ // ------------------------------------------------------------------------
+ // Fields
+ // ------------------------------------------------------------------------
+
+ /**
+ * The sort of this Java type.
+ */
+ private final int sort;
+
+ /**
+ * A buffer containing the internal name of this Java type. This field is
+ * only used for reference types.
+ */
+ private final char[] buf;
+
+ /**
+ * The offset of the internal name of this Java type in {@link #buf buf}.
+ * This field is only used for reference types.
+ */
+ private final int off;
+
+ /**
+ * The length of the internal name of this Java type. This field is only
+ * used for reference types.
+ */
+ private final int len;
+
+ // ------------------------------------------------------------------------
+ // Constructors
+ // ------------------------------------------------------------------------
+
+ /**
+ * Constructs a primitive type.
+ *
+ * @param sort the sort of the primitive type to be constructed.
+ */
+ private Type(final int sort) {
+ this(sort, null, 0, 1);
+ }
+
+ /**
+ * Constructs a reference type.
+ *
+ * @param sort the sort of the reference type to be constructed.
+ * @param buf a buffer containing the descriptor of the previous type.
+ * @param off the offset of this descriptor in the previous buffer.
+ * @param len the length of this descriptor.
+ */
+ private Type(final int sort, final char[] buf, final int off, final int len)
+ {
+ this.sort = sort;
+ this.buf = buf;
+ this.off = off;
+ this.len = len;
+ }
+
+ /**
+ * Returns the Java type corresponding to the given type descriptor.
+ *
+ * @param typeDescriptor a type descriptor.
+ * @return the Java type corresponding to the given type descriptor.
+ */
+ public static Type getType(final String typeDescriptor) {
+ return getType(typeDescriptor.toCharArray(), 0);
+ }
+
+ /**
+ * Returns the Java type corresponding to the given internal name.
+ *
+ * @param internalName an internal name.
+ * @return the Java type corresponding to the given internal name.
+ */
+ public static Type getObjectType(final String internalName) {
+ char[] buf = internalName.toCharArray();
+ return new Type(buf[0] == '[' ? ARRAY : OBJECT, buf, 0, buf.length);
+ }
+
+ /**
+ * Returns the Java type corresponding to the given class.
+ *
+ * @param c a class.
+ * @return the Java type corresponding to the given class.
+ */
+ public static Type getType(final Class c) {
+ if (c.isPrimitive()) {
+ if (c == Integer.TYPE) {
+ return INT_TYPE;
+ } else if (c == Void.TYPE) {
+ return VOID_TYPE;
+ } else if (c == Boolean.TYPE) {
+ return BOOLEAN_TYPE;
+ } else if (c == Byte.TYPE) {
+ return BYTE_TYPE;
+ } else if (c == Character.TYPE) {
+ return CHAR_TYPE;
+ } else if (c == Short.TYPE) {
+ return SHORT_TYPE;
+ } else if (c == Double.TYPE) {
+ return DOUBLE_TYPE;
+ } else if (c == Float.TYPE) {
+ return FLOAT_TYPE;
+ } else /* if (c == Long.TYPE) */{
+ return LONG_TYPE;
+ }
+ } else {
+ return getType(getDescriptor(c));
+ }
+ }
+
+ /**
+ * Returns the Java types corresponding to the argument types of the given
+ * method descriptor.
+ *
+ * @param methodDescriptor a method descriptor.
+ * @return the Java types corresponding to the argument types of the given
+ * method descriptor.
+ */
+ public static Type[] getArgumentTypes(final String methodDescriptor) {
+ char[] buf = methodDescriptor.toCharArray();
+ int off = 1;
+ int size = 0;
+ while (true) {
+ char car = buf[off++];
+ if (car == ')') {
+ break;
+ } else if (car == 'L') {
+ while (buf[off++] != ';') {
+ }
+ ++size;
+ } else if (car != '[') {
+ ++size;
+ }
+ }
+ Type[] args = new Type[size];
+ off = 1;
+ size = 0;
+ while (buf[off] != ')') {
+ args[size] = getType(buf, off);
+ off += args[size].len + (args[size].sort == OBJECT ? 2 : 0);
+ size += 1;
+ }
+ return args;
+ }
+
+ /**
+ * Returns the Java types corresponding to the argument types of the given
+ * method.
+ *
+ * @param method a method.
+ * @return the Java types corresponding to the argument types of the given
+ * method.
+ */
+ public static Type[] getArgumentTypes(final Method method) {
+ Class[] classes = method.getParameterTypes();
+ Type[] types = new Type[classes.length];
+ for (int i = classes.length - 1; i >= 0; --i) {
+ types[i] = getType(classes[i]);
+ }
+ return types;
+ }
+
+ /**
+ * Returns the Java type corresponding to the return type of the given
+ * method descriptor.
+ *
+ * @param methodDescriptor a method descriptor.
+ * @return the Java type corresponding to the return type of the given
+ * method descriptor.
+ */
+ public static Type getReturnType(final String methodDescriptor) {
+ char[] buf = methodDescriptor.toCharArray();
+ return getType(buf, methodDescriptor.indexOf(')') + 1);
+ }
+
+ /**
+ * Returns the Java type corresponding to the return type of the given
+ * method.
+ *
+ * @param method a method.
+ * @return the Java type corresponding to the return type of the given
+ * method.
+ */
+ public static Type getReturnType(final Method method) {
+ return getType(method.getReturnType());
+ }
+
+ /**
+ * Returns the Java type corresponding to the given type descriptor.
+ *
+ * @param buf a buffer containing a type descriptor.
+ * @param off the offset of this descriptor in the previous buffer.
+ * @return the Java type corresponding to the given type descriptor.
+ */
+ private static Type getType(final char[] buf, final int off) {
+ int len;
+ switch (buf[off]) {
+ case 'V':
+ return VOID_TYPE;
+ case 'Z':
+ return BOOLEAN_TYPE;
+ case 'C':
+ return CHAR_TYPE;
+ case 'B':
+ return BYTE_TYPE;
+ case 'S':
+ return SHORT_TYPE;
+ case 'I':
+ return INT_TYPE;
+ case 'F':
+ return FLOAT_TYPE;
+ case 'J':
+ return LONG_TYPE;
+ case 'D':
+ return DOUBLE_TYPE;
+ case '[':
+ len = 1;
+ while (buf[off + len] == '[') {
+ ++len;
+ }
+ if (buf[off + len] == 'L') {
+ ++len;
+ while (buf[off + len] != ';') {
+ ++len;
+ }
+ }
+ return new Type(ARRAY, buf, off, len + 1);
+ // case 'L':
+ default:
+ len = 1;
+ while (buf[off + len] != ';') {
+ ++len;
+ }
+ return new Type(OBJECT, buf, off + 1, len - 1);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Accessors
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the sort of this Java type.
+ *
+ * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN},
+ * {@link #CHAR CHAR}, {@link #BYTE BYTE}, {@link #SHORT SHORT},
+ * {@link #INT INT}, {@link #FLOAT FLOAT}, {@link #LONG LONG},
+ * {@link #DOUBLE DOUBLE}, {@link #ARRAY ARRAY} or
+ * {@link #OBJECT OBJECT}.
+ */
+ public int getSort() {
+ return sort;
+ }
+
+ /**
+ * Returns the number of dimensions of this array type. This method should
+ * only be used for an array type.
+ *
+ * @return the number of dimensions of this array type.
+ */
+ public int getDimensions() {
+ int i = 1;
+ while (buf[off + i] == '[') {
+ ++i;
+ }
+ return i;
+ }
+
+ /**
+ * Returns the type of the elements of this array type. This method should
+ * only be used for an array type.
+ *
+ * @return Returns the type of the elements of this array type.
+ */
+ public Type getElementType() {
+ return getType(buf, off + getDimensions());
+ }
+
+ /**
+ * Returns the name of the class corresponding to this type.
+ *
+ * @return the fully qualified name of the class corresponding to this type.
+ */
+ public String getClassName() {
+ switch (sort) {
+ case VOID:
+ return "void";
+ case BOOLEAN:
+ return "boolean";
+ case CHAR:
+ return "char";
+ case BYTE:
+ return "byte";
+ case SHORT:
+ return "short";
+ case INT:
+ return "int";
+ case FLOAT:
+ return "float";
+ case LONG:
+ return "long";
+ case DOUBLE:
+ return "double";
+ case ARRAY:
+ StringBuffer b = new StringBuffer(getElementType().getClassName());
+ for (int i = getDimensions(); i > 0; --i) {
+ b.append("[]");
+ }
+ return b.toString();
+ // case OBJECT:
+ default:
+ return new String(buf, off, len).replace('/', '.');
+ }
+ }
+
+ /**
+ * Returns the internal name of the class corresponding to this object or
+ * array type. The internal name of a class is its fully qualified name (as
+ * returned by Class.getName(), where '.' are replaced by '/'. This method
+ * should only be used for an object or array type.
+ *
+ * @return the internal name of the class corresponding to this object type.
+ */
+ public String getInternalName() {
+ return new String(buf, off, len);
+ }
+
+ // ------------------------------------------------------------------------
+ // Conversion to type descriptors
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the descriptor corresponding to this Java type.
+ *
+ * @return the descriptor corresponding to this Java type.
+ */
+ public String getDescriptor() {
+ StringBuffer buf = new StringBuffer();
+ getDescriptor(buf);
+ return buf.toString();
+ }
+
+ /**
+ * Returns the descriptor corresponding to the given argument and return
+ * types.
+ *
+ * @param returnType the return type of the method.
+ * @param argumentTypes the argument types of the method.
+ * @return the descriptor corresponding to the given argument and return
+ * types.
+ */
+ public static String getMethodDescriptor(
+ final Type returnType,
+ final Type[] argumentTypes)
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append('(');
+ for (int i = 0; i < argumentTypes.length; ++i) {
+ argumentTypes[i].getDescriptor(buf);
+ }
+ buf.append(')');
+ returnType.getDescriptor(buf);
+ return buf.toString();
+ }
+
+ /**
+ * Appends the descriptor corresponding to this Java type to the given
+ * string buffer.
+ *
+ * @param buf the string buffer to which the descriptor must be appended.
+ */
+ private void getDescriptor(final StringBuffer buf) {
+ switch (sort) {
+ case VOID:
+ buf.append('V');
+ return;
+ case BOOLEAN:
+ buf.append('Z');
+ return;
+ case CHAR:
+ buf.append('C');
+ return;
+ case BYTE:
+ buf.append('B');
+ return;
+ case SHORT:
+ buf.append('S');
+ return;
+ case INT:
+ buf.append('I');
+ return;
+ case FLOAT:
+ buf.append('F');
+ return;
+ case LONG:
+ buf.append('J');
+ return;
+ case DOUBLE:
+ buf.append('D');
+ return;
+ case ARRAY:
+ buf.append(this.buf, off, len);
+ return;
+ // case OBJECT:
+ default:
+ buf.append('L');
+ buf.append(this.buf, off, len);
+ buf.append(';');
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Direct conversion from classes to type descriptors,
+ // without intermediate Type objects
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the internal name of the given class. The internal name of a
+ * class is its fully qualified name, as returned by Class.getName(), where
+ * '.' are replaced by '/'.
+ *
+ * @param c an object or array class.
+ * @return the internal name of the given class.
+ */
+ public static String getInternalName(final Class c) {
+ return c.getName().replace('.', '/');
+ }
+
+ /**
+ * Returns the descriptor corresponding to the given Java type.
+ *
+ * @param c an object class, a primitive class or an array class.
+ * @return the descriptor corresponding to the given class.
+ */
+ public static String getDescriptor(final Class c) {
+ StringBuffer buf = new StringBuffer();
+ getDescriptor(buf, c);
+ return buf.toString();
+ }
+
+ /**
+ * Returns the descriptor corresponding to the given constructor.
+ *
+ * @param c a {@link Constructor Constructor} object.
+ * @return the descriptor of the given constructor.
+ */
+ public static String getConstructorDescriptor(final Constructor c) {
+ Class[] parameters = c.getParameterTypes();
+ StringBuffer buf = new StringBuffer();
+ buf.append('(');
+ for (int i = 0; i < parameters.length; ++i) {
+ getDescriptor(buf, parameters[i]);
+ }
+ return buf.append(")V").toString();
+ }
+
+ /**
+ * Returns the descriptor corresponding to the given method.
+ *
+ * @param m a {@link Method Method} object.
+ * @return the descriptor of the given method.
+ */
+ public static String getMethodDescriptor(final Method m) {
+ Class[] parameters = m.getParameterTypes();
+ StringBuffer buf = new StringBuffer();
+ buf.append('(');
+ for (int i = 0; i < parameters.length; ++i) {
+ getDescriptor(buf, parameters[i]);
+ }
+ buf.append(')');
+ getDescriptor(buf, m.getReturnType());
+ return buf.toString();
+ }
+
+ /**
+ * Appends the descriptor of the given class to the given string buffer.
+ *
+ * @param buf the string buffer to which the descriptor must be appended.
+ * @param c the class whose descriptor must be computed.
+ */
+ private static void getDescriptor(final StringBuffer buf, final Class c) {
+ Class d = c;
+ while (true) {
+ if (d.isPrimitive()) {
+ char car;
+ if (d == Integer.TYPE) {
+ car = 'I';
+ } else if (d == Void.TYPE) {
+ car = 'V';
+ } else if (d == Boolean.TYPE) {
+ car = 'Z';
+ } else if (d == Byte.TYPE) {
+ car = 'B';
+ } else if (d == Character.TYPE) {
+ car = 'C';
+ } else if (d == Short.TYPE) {
+ car = 'S';
+ } else if (d == Double.TYPE) {
+ car = 'D';
+ } else if (d == Float.TYPE) {
+ car = 'F';
+ } else /* if (d == Long.TYPE) */{
+ car = 'J';
+ }
+ buf.append(car);
+ return;
+ } else if (d.isArray()) {
+ buf.append('[');
+ d = d.getComponentType();
+ } else {
+ buf.append('L');
+ String name = d.getName();
+ int len = name.length();
+ for (int i = 0; i < len; ++i) {
+ char car = name.charAt(i);
+ buf.append(car == '.' ? '/' : car);
+ }
+ buf.append(';');
+ return;
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Corresponding size and opcodes
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the size of values of this type.
+ *
+ * @return the size of values of this type, i.e., 2 for <tt>long</tt> and
+ * <tt>double</tt>, and 1 otherwise.
+ */
+ public int getSize() {
+ return sort == LONG || sort == DOUBLE ? 2 : 1;
+ }
+
+ /**
+ * Returns a JVM instruction opcode adapted to this Java type.
+ *
+ * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD,
+ * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL,
+ * ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
+ * @return an opcode that is similar to the given opcode, but adapted to
+ * this Java type. For example, if this type is <tt>float</tt> and
+ * <tt>opcode</tt> is IRETURN, this method returns FRETURN.
+ */
+ public int getOpcode(final int opcode) {
+ if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
+ switch (sort) {
+ case BOOLEAN:
+ case BYTE:
+ return opcode + 5;
+ case CHAR:
+ return opcode + 6;
+ case SHORT:
+ return opcode + 7;
+ case INT:
+ return opcode;
+ case FLOAT:
+ return opcode + 2;
+ case LONG:
+ return opcode + 1;
+ case DOUBLE:
+ return opcode + 3;
+ // case ARRAY:
+ // case OBJECT:
+ default:
+ return opcode + 4;
+ }
+ } else {
+ switch (sort) {
+ case VOID:
+ return opcode + 5;
+ case BOOLEAN:
+ case CHAR:
+ case BYTE:
+ case SHORT:
+ case INT:
+ return opcode;
+ case FLOAT:
+ return opcode + 2;
+ case LONG:
+ return opcode + 1;
+ case DOUBLE:
+ return opcode + 3;
+ // case ARRAY:
+ // case OBJECT:
+ default:
+ return opcode + 4;
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Equals, hashCode and toString
+ // ------------------------------------------------------------------------
+
+ /**
+ * Tests if the given object is equal to this type.
+ *
+ * @param o the object to be compared to this type.
+ * @return <tt>true</tt> if the given object is equal to this type.
+ */
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Type)) {
+ return false;
+ }
+ Type t = (Type) o;
+ if (sort != t.sort) {
+ return false;
+ }
+ if (sort == OBJECT || sort == ARRAY) {
+ if (len != t.len) {
+ return false;
+ }
+ for (int i = off, j = t.off, end = i + len; i < end; i++, j++) {
+ if (buf[i] != t.buf[j]) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns a hash code value for this type.
+ *
+ * @return a hash code value for this type.
+ */
+ public int hashCode() {
+ int hc = 13 * sort;
+ if (sort == OBJECT || sort == ARRAY) {
+ for (int i = off, end = i + len; i < end; i++) {
+ hc = 17 * (hc + buf[i]);
+ }
+ }
+ return hc;
+ }
+
+ /**
+ * Returns a string representation of this type.
+ *
+ * @return the descriptor of this type.
+ */
+ public String toString() {
+ return getDescriptor();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/package.html b/cglib-and-asm/src/org/mockito/asm/package.html
new file mode 100644
index 0000000..b87e4f2
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/package.html
@@ -0,0 +1,87 @@
+<html>
+<!--
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<body>
+Provides a small and fast bytecode manipulation framework.
+
+<p>
+The <a href="http://www.objectweb.org/asm">ASM</a> framework is organized
+around the {@link org.objectweb.asm.ClassVisitor ClassVisitor},
+{@link org.objectweb.asm.FieldVisitor FieldVisitor} and
+{@link org.objectweb.asm.MethodVisitor MethodVisitor} interfaces, which allow
+one to visit the fields and methods of a class, including the bytecode
+instructions of each method.
+
+<p>
+In addition to these main interfaces, ASM provides a {@link
+org.objectweb.asm.ClassReader ClassReader} class, that can parse an
+existing class and make a given visitor visit it. ASM also provides
+a {@link org.objectweb.asm.ClassWriter ClassWriter} class, which is
+a visitor that generates Java class files.
+
+<p>
+In order to generate a class from scratch, only the {@link
+org.objectweb.asm.ClassWriter ClassWriter} class is necessary. Indeed,
+in order to generate a class, one must just call its visit<i>XXX</i>
+methods with the appropriate arguments to generate the desired fields
+and methods. See the "helloworld" example in the ASM distribution for
+more details about class generation.
+
+<p>
+In order to modify existing classes, one must use a {@link
+org.objectweb.asm.ClassReader ClassReader} class to analyze
+the original class, a class modifier, and a {@link org.objectweb.asm.ClassWriter
+ClassWriter} to construct the modified class. The class modifier
+is just a {@link org.objectweb.asm.ClassVisitor ClassVisitor}
+that delegates most of the work to another {@link org.objectweb.asm.ClassVisitor
+ClassVisitor}, but that sometimes changes some parameter values,
+or call additional methods, in order to implement the desired
+modification process. In order to make it easier to implement such
+class modifiers, ASM provides the {@link org.objectweb.asm.ClassAdapter
+ClassAdapter} and {@link org.objectweb.asm.MethodAdapter MethodAdapter}
+classes, which implement the {@link org.objectweb.asm.ClassVisitor ClassVisitor}
+and {@link org.objectweb.asm.MethodVisitor MethodVisitor} interfaces by
+delegating all work to other visitors. See the "adapt" example in the ASM
+distribution for more details about class modification.
+
+<p>
+The size of the core ASM library, <tt>asm.jar</tt>, is only 42KB, which is much
+smaller than the size of the
+<a href="http://jakarta.apache.org/bcel">BCEL</a> library (504KB), and than the
+size of the
+<a href="http://serp.sourceforge.net">SERP</a> library (150KB). ASM is also
+much faster than these tools. Indeed the overhead of a load time class
+transformation process is of the order of 60% with ASM, 700% or more with BCEL,
+and 1100% or more with SERP (see the <tt>test/perf</tt> directory in the ASM
+distribution)!
+
+@since ASM 1.3
+</body>
+</html>
diff --git a/cglib-and-asm/src/org/mockito/asm/signature/SignatureReader.java b/cglib-and-asm/src/org/mockito/asm/signature/SignatureReader.java
new file mode 100644
index 0000000..5f07eb8
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/signature/SignatureReader.java
@@ -0,0 +1,229 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.signature;
+
+/**
+ * A type signature parser to make a signature visitor visit an existing
+ * signature.
+ *
+ * @author Thomas Hallgren
+ * @author Eric Bruneton
+ */
+public class SignatureReader {
+
+ /**
+ * The signature to be read.
+ */
+ private final String signature;
+
+ /**
+ * Constructs a {@link SignatureReader} for the given signature.
+ *
+ * @param signature A <i>ClassSignature</i>, <i>MethodTypeSignature</i>,
+ * or <i>FieldTypeSignature</i>.
+ */
+ public SignatureReader(final String signature) {
+ this.signature = signature;
+ }
+
+ /**
+ * Makes the given visitor visit the signature of this
+ * {@link SignatureReader}. This signature is the one specified in the
+ * constructor (see {@link #SignatureReader(String) SignatureReader}). This
+ * method is intended to be called on a {@link SignatureReader} that was
+ * created using a <i>ClassSignature</i> (such as the
+ * <code>signature</code> parameter of the
+ * {@link org.mockito.asm.ClassVisitor#visit ClassVisitor.visit} method)
+ * or a <i>MethodTypeSignature</i> (such as the <code>signature</code>
+ * parameter of the
+ * {@link org.mockito.asm.ClassVisitor#visitMethod ClassVisitor.visitMethod}
+ * method).
+ *
+ * @param v the visitor that must visit this signature.
+ */
+ public void accept(final SignatureVisitor v) {
+ String signature = this.signature;
+ int len = signature.length();
+ int pos;
+ char c;
+
+ if (signature.charAt(0) == '<') {
+ pos = 2;
+ do {
+ int end = signature.indexOf(':', pos);
+ v.visitFormalTypeParameter(signature.substring(pos - 1, end));
+ pos = end + 1;
+
+ c = signature.charAt(pos);
+ if (c == 'L' || c == '[' || c == 'T') {
+ pos = parseType(signature, pos, v.visitClassBound());
+ }
+
+ while ((c = signature.charAt(pos++)) == ':') {
+ pos = parseType(signature, pos, v.visitInterfaceBound());
+ }
+ } while (c != '>');
+ } else {
+ pos = 0;
+ }
+
+ if (signature.charAt(pos) == '(') {
+ pos++;
+ while (signature.charAt(pos) != ')') {
+ pos = parseType(signature, pos, v.visitParameterType());
+ }
+ pos = parseType(signature, pos + 1, v.visitReturnType());
+ while (pos < len) {
+ pos = parseType(signature, pos + 1, v.visitExceptionType());
+ }
+ } else {
+ pos = parseType(signature, pos, v.visitSuperclass());
+ while (pos < len) {
+ pos = parseType(signature, pos, v.visitInterface());
+ }
+ }
+ }
+
+ /**
+ * Makes the given visitor visit the signature of this
+ * {@link SignatureReader}. This signature is the one specified in the
+ * constructor (see {@link #SignatureReader(String) SignatureReader}). This
+ * method is intended to be called on a {@link SignatureReader} that was
+ * created using a <i>FieldTypeSignature</i>, such as the
+ * <code>signature</code> parameter of the
+ * {@link org.mockito.asm.ClassVisitor#visitField
+ * ClassVisitor.visitField} or {@link
+ * org.mockito.asm.MethodVisitor#visitLocalVariable
+ * MethodVisitor.visitLocalVariable} methods.
+ *
+ * @param v the visitor that must visit this signature.
+ */
+ public void acceptType(final SignatureVisitor v) {
+ parseType(this.signature, 0, v);
+ }
+
+ /**
+ * Parses a field type signature and makes the given visitor visit it.
+ *
+ * @param signature a string containing the signature that must be parsed.
+ * @param pos index of the first character of the signature to parsed.
+ * @param v the visitor that must visit this signature.
+ * @return the index of the first character after the parsed signature.
+ */
+ private static int parseType(
+ final String signature,
+ int pos,
+ final SignatureVisitor v)
+ {
+ char c;
+ int start, end;
+ boolean visited, inner;
+ String name;
+
+ switch (c = signature.charAt(pos++)) {
+ case 'Z':
+ case 'C':
+ case 'B':
+ case 'S':
+ case 'I':
+ case 'F':
+ case 'J':
+ case 'D':
+ case 'V':
+ v.visitBaseType(c);
+ return pos;
+
+ case '[':
+ return parseType(signature, pos, v.visitArrayType());
+
+ case 'T':
+ end = signature.indexOf(';', pos);
+ v.visitTypeVariable(signature.substring(pos, end));
+ return end + 1;
+
+ default: // case 'L':
+ start = pos;
+ visited = false;
+ inner = false;
+ for (;;) {
+ switch (c = signature.charAt(pos++)) {
+ case '.':
+ case ';':
+ if (!visited) {
+ name = signature.substring(start, pos - 1);
+ if (inner) {
+ v.visitInnerClassType(name);
+ } else {
+ v.visitClassType(name);
+ }
+ }
+ if (c == ';') {
+ v.visitEnd();
+ return pos;
+ }
+ start = pos;
+ visited = false;
+ inner = true;
+ break;
+
+ case '<':
+ name = signature.substring(start, pos - 1);
+ if (inner) {
+ v.visitInnerClassType(name);
+ } else {
+ v.visitClassType(name);
+ }
+ visited = true;
+ top: for (;;) {
+ switch (c = signature.charAt(pos)) {
+ case '>':
+ break top;
+ case '*':
+ ++pos;
+ v.visitTypeArgument();
+ break;
+ case '+':
+ case '-':
+ pos = parseType(signature,
+ pos + 1,
+ v.visitTypeArgument(c));
+ break;
+ default:
+ pos = parseType(signature,
+ pos,
+ v.visitTypeArgument('='));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/signature/SignatureVisitor.java b/cglib-and-asm/src/org/mockito/asm/signature/SignatureVisitor.java
new file mode 100644
index 0000000..d943e92
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/signature/SignatureVisitor.java
@@ -0,0 +1,185 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.signature;
+
+/**
+ * A visitor to visit a generic signature. The methods of this interface must be
+ * called in one of the three following orders (the last one is the only valid
+ * order for a {@link SignatureVisitor} that is returned by a method of this
+ * interface): <ul> <li><i>ClassSignature</i> = (
+ * <tt>visitFormalTypeParameter</tt>
+ * <tt>visitClassBound</tt>?
+ * <tt>visitInterfaceBound</tt>* )* ( <tt>visitSuperClass</tt>
+ * <tt>visitInterface</tt>* )</li>
+ * <li><i>MethodSignature</i> = ( <tt>visitFormalTypeParameter</tt>
+ * <tt>visitClassBound</tt>?
+ * <tt>visitInterfaceBound</tt>* )* ( <tt>visitParameterType</tt>*
+ * <tt>visitReturnType</tt>
+ * <tt>visitExceptionType</tt>* )</li> <li><i>TypeSignature</i> =
+ * <tt>visitBaseType</tt> | <tt>visitTypeVariable</tt> |
+ * <tt>visitArrayType</tt> | (
+ * <tt>visitClassType</tt> <tt>visitTypeArgument</tt>* (
+ * <tt>visitInnerClassType</tt> <tt>visitTypeArgument</tt>* )*
+ * <tt>visitEnd</tt> ) )</li> </ul>
+ *
+ * @author Thomas Hallgren
+ * @author Eric Bruneton
+ */
+public interface SignatureVisitor {
+
+ /**
+ * Wildcard for an "extends" type argument.
+ */
+ char EXTENDS = '+';
+
+ /**
+ * Wildcard for a "super" type argument.
+ */
+ char SUPER = '-';
+
+ /**
+ * Wildcard for a normal type argument.
+ */
+ char INSTANCEOF = '=';
+
+ /**
+ * Visits a formal type parameter.
+ *
+ * @param name the name of the formal parameter.
+ */
+ void visitFormalTypeParameter(String name);
+
+ /**
+ * Visits the class bound of the last visited formal type parameter.
+ *
+ * @return a non null visitor to visit the signature of the class bound.
+ */
+ SignatureVisitor visitClassBound();
+
+ /**
+ * Visits an interface bound of the last visited formal type parameter.
+ *
+ * @return a non null visitor to visit the signature of the interface bound.
+ */
+ SignatureVisitor visitInterfaceBound();
+
+ /**
+ * Visits the type of the super class.
+ *
+ * @return a non null visitor to visit the signature of the super class
+ * type.
+ */
+ SignatureVisitor visitSuperclass();
+
+ /**
+ * Visits the type of an interface implemented by the class.
+ *
+ * @return a non null visitor to visit the signature of the interface type.
+ */
+ SignatureVisitor visitInterface();
+
+ /**
+ * Visits the type of a method parameter.
+ *
+ * @return a non null visitor to visit the signature of the parameter type.
+ */
+ SignatureVisitor visitParameterType();
+
+ /**
+ * Visits the return type of the method.
+ *
+ * @return a non null visitor to visit the signature of the return type.
+ */
+ SignatureVisitor visitReturnType();
+
+ /**
+ * Visits the type of a method exception.
+ *
+ * @return a non null visitor to visit the signature of the exception type.
+ */
+ SignatureVisitor visitExceptionType();
+
+ /**
+ * Visits a signature corresponding to a primitive type.
+ *
+ * @param descriptor the descriptor of the primitive type, or 'V' for
+ * <tt>void</tt>.
+ */
+ void visitBaseType(char descriptor);
+
+ /**
+ * Visits a signature corresponding to a type variable.
+ *
+ * @param name the name of the type variable.
+ */
+ void visitTypeVariable(String name);
+
+ /**
+ * Visits a signature corresponding to an array type.
+ *
+ * @return a non null visitor to visit the signature of the array element
+ * type.
+ */
+ SignatureVisitor visitArrayType();
+
+ /**
+ * Starts the visit of a signature corresponding to a class or interface
+ * type.
+ *
+ * @param name the internal name of the class or interface.
+ */
+ void visitClassType(String name);
+
+ /**
+ * Visits an inner class.
+ *
+ * @param name the local name of the inner class in its enclosing class.
+ */
+ void visitInnerClassType(String name);
+
+ /**
+ * Visits an unbounded type argument of the last visited class or inner
+ * class type.
+ */
+ void visitTypeArgument();
+
+ /**
+ * Visits a type argument of the last visited class or inner class type.
+ *
+ * @param wildcard '+', '-' or '='.
+ * @return a non null visitor to visit the signature of the type argument.
+ */
+ SignatureVisitor visitTypeArgument(char wildcard);
+
+ /**
+ * Ends the visit of a signature corresponding to a class or interface type.
+ */
+ void visitEnd();
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/signature/SignatureWriter.java b/cglib-and-asm/src/org/mockito/asm/signature/SignatureWriter.java
new file mode 100644
index 0000000..77f2da9
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/signature/SignatureWriter.java
@@ -0,0 +1,207 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.signature;
+
+/**
+ * A signature visitor that generates signatures in string format.
+ *
+ * @author Thomas Hallgren
+ * @author Eric Bruneton
+ */
+public class SignatureWriter implements SignatureVisitor {
+
+ /**
+ * Buffer used to construct the signature.
+ */
+ private final StringBuffer buf = new StringBuffer();
+
+ /**
+ * Indicates if the signature contains formal type parameters.
+ */
+ private boolean hasFormals;
+
+ /**
+ * Indicates if the signature contains method parameter types.
+ */
+ private boolean hasParameters;
+
+ /**
+ * Stack used to keep track of class types that have arguments. Each element
+ * of this stack is a boolean encoded in one bit. The top of the stack is
+ * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
+ * /2.
+ */
+ private int argumentStack;
+
+ /**
+ * Constructs a new {@link SignatureWriter} object.
+ */
+ public SignatureWriter() {
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the SignatureVisitor interface
+ // ------------------------------------------------------------------------
+
+ public void visitFormalTypeParameter(final String name) {
+ if (!hasFormals) {
+ hasFormals = true;
+ buf.append('<');
+ }
+ buf.append(name);
+ buf.append(':');
+ }
+
+ public SignatureVisitor visitClassBound() {
+ return this;
+ }
+
+ public SignatureVisitor visitInterfaceBound() {
+ buf.append(':');
+ return this;
+ }
+
+ public SignatureVisitor visitSuperclass() {
+ endFormals();
+ return this;
+ }
+
+ public SignatureVisitor visitInterface() {
+ return this;
+ }
+
+ public SignatureVisitor visitParameterType() {
+ endFormals();
+ if (!hasParameters) {
+ hasParameters = true;
+ buf.append('(');
+ }
+ return this;
+ }
+
+ public SignatureVisitor visitReturnType() {
+ endFormals();
+ if (!hasParameters) {
+ buf.append('(');
+ }
+ buf.append(')');
+ return this;
+ }
+
+ public SignatureVisitor visitExceptionType() {
+ buf.append('^');
+ return this;
+ }
+
+ public void visitBaseType(final char descriptor) {
+ buf.append(descriptor);
+ }
+
+ public void visitTypeVariable(final String name) {
+ buf.append('T');
+ buf.append(name);
+ buf.append(';');
+ }
+
+ public SignatureVisitor visitArrayType() {
+ buf.append('[');
+ return this;
+ }
+
+ public void visitClassType(final String name) {
+ buf.append('L');
+ buf.append(name);
+ argumentStack *= 2;
+ }
+
+ public void visitInnerClassType(final String name) {
+ endArguments();
+ buf.append('.');
+ buf.append(name);
+ argumentStack *= 2;
+ }
+
+ public void visitTypeArgument() {
+ if (argumentStack % 2 == 0) {
+ ++argumentStack;
+ buf.append('<');
+ }
+ buf.append('*');
+ }
+
+ public SignatureVisitor visitTypeArgument(final char wildcard) {
+ if (argumentStack % 2 == 0) {
+ ++argumentStack;
+ buf.append('<');
+ }
+ if (wildcard != '=') {
+ buf.append(wildcard);
+ }
+ return this;
+ }
+
+ public void visitEnd() {
+ endArguments();
+ buf.append(';');
+ }
+
+ /**
+ * Returns the signature that was built by this signature writer.
+ *
+ * @return the signature that was built by this signature writer.
+ */
+ public String toString() {
+ return buf.toString();
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Ends the formal type parameters section of the signature.
+ */
+ private void endFormals() {
+ if (hasFormals) {
+ hasFormals = false;
+ buf.append('>');
+ }
+ }
+
+ /**
+ * Ends the type arguments of a class or inner class type.
+ */
+ private void endArguments() {
+ if (argumentStack % 2 != 0) {
+ buf.append('>');
+ }
+ argumentStack /= 2;
+ }
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/signature/package.html b/cglib-and-asm/src/org/mockito/asm/signature/package.html
new file mode 100644
index 0000000..b9de4cf
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/signature/package.html
@@ -0,0 +1,36 @@
+<html>
+<!--
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<body>
+Provides support for type signatures.
+
+@since ASM 2.0
+</body>
+</html>
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/AbstractInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/AbstractInsnNode.java
new file mode 100644
index 0000000..191777a
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/AbstractInsnNode.java
@@ -0,0 +1,233 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.List;
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A node that represents a bytecode instruction. <i>An instruction can appear
+ * at most once in at most one {@link InsnList} at a time</i>.
+ *
+ * @author Eric Bruneton
+ */
+public abstract class AbstractInsnNode {
+
+ /**
+ * The type of {@link InsnNode} instructions.
+ */
+ public static final int INSN = 0;
+
+ /**
+ * The type of {@link IntInsnNode} instructions.
+ */
+ public static final int INT_INSN = 1;
+
+ /**
+ * The type of {@link VarInsnNode} instructions.
+ */
+ public static final int VAR_INSN = 2;
+
+ /**
+ * The type of {@link TypeInsnNode} instructions.
+ */
+ public static final int TYPE_INSN = 3;
+
+ /**
+ * The type of {@link FieldInsnNode} instructions.
+ */
+ public static final int FIELD_INSN = 4;
+
+ /**
+ * The type of {@link MethodInsnNode} instructions.
+ */
+ public static final int METHOD_INSN = 5;
+
+ /**
+ * The type of {@link JumpInsnNode} instructions.
+ */
+ public static final int JUMP_INSN = 6;
+
+ /**
+ * The type of {@link LabelNode} "instructions".
+ */
+ public static final int LABEL = 7;
+
+ /**
+ * The type of {@link LdcInsnNode} instructions.
+ */
+ public static final int LDC_INSN = 8;
+
+ /**
+ * The type of {@link IincInsnNode} instructions.
+ */
+ public static final int IINC_INSN = 9;
+
+ /**
+ * The type of {@link TableSwitchInsnNode} instructions.
+ */
+ public static final int TABLESWITCH_INSN = 10;
+
+ /**
+ * The type of {@link LookupSwitchInsnNode} instructions.
+ */
+ public static final int LOOKUPSWITCH_INSN = 11;
+
+ /**
+ * The type of {@link MultiANewArrayInsnNode} instructions.
+ */
+ public static final int MULTIANEWARRAY_INSN = 12;
+
+ /**
+ * The type of {@link FrameNode} "instructions".
+ */
+ public static final int FRAME = 13;
+
+ /**
+ * The type of {@link LineNumberNode} "instructions".
+ */
+ public static final int LINE = 14;
+
+ /**
+ * The opcode of this instruction.
+ */
+ protected int opcode;
+
+ /**
+ * Previous instruction in the list to which this instruction belongs.
+ */
+ AbstractInsnNode prev;
+
+ /**
+ * Next instruction in the list to which this instruction belongs.
+ */
+ AbstractInsnNode next;
+
+ /**
+ * Index of this instruction in the list to which it belongs. The value of
+ * this field is correct only when {@link InsnList#cache} is not null. A
+ * value of -1 indicates that this instruction does not belong to any
+ * {@link InsnList}.
+ */
+ int index;
+
+ /**
+ * Constructs a new {@link AbstractInsnNode}.
+ *
+ * @param opcode the opcode of the instruction to be constructed.
+ */
+ protected AbstractInsnNode(final int opcode) {
+ this.opcode = opcode;
+ this.index = -1;
+ }
+
+ /**
+ * Returns the opcode of this instruction.
+ *
+ * @return the opcode of this instruction.
+ */
+ public int getOpcode() {
+ return opcode;
+ }
+
+ /**
+ * Returns the type of this instruction.
+ *
+ * @return the type of this instruction, i.e. one the constants defined in
+ * this class.
+ */
+ public abstract int getType();
+
+ /**
+ * Returns the previous instruction in the list to which this instruction
+ * belongs, if any.
+ *
+ * @return the previous instruction in the list to which this instruction
+ * belongs, if any. May be <tt>null</tt>.
+ */
+ public AbstractInsnNode getPrevious() {
+ return prev;
+ }
+
+ /**
+ * Returns the next instruction in the list to which this instruction
+ * belongs, if any.
+ *
+ * @return the next instruction in the list to which this instruction
+ * belongs, if any. May be <tt>null</tt>.
+ */
+ public AbstractInsnNode getNext() {
+ return next;
+ }
+
+ /**
+ * Makes the given code visitor visit this instruction.
+ *
+ * @param cv a code visitor.
+ */
+ public abstract void accept(final MethodVisitor cv);
+
+ /**
+ * Returns a copy of this instruction.
+ *
+ * @param labels a map from LabelNodes to cloned LabelNodes.
+ * @return a copy of this instruction. The returned instruction does not
+ * belong to any {@link InsnList}.
+ */
+ public abstract AbstractInsnNode clone(final Map labels);
+
+ /**
+ * Returns the clone of the given label.
+ *
+ * @param label a label.
+ * @param map a map from LabelNodes to cloned LabelNodes.
+ * @return the clone of the given label.
+ */
+ static LabelNode clone(final LabelNode label, final Map map) {
+ return (LabelNode) map.get(label);
+ }
+
+ /**
+ * Returns the clones of the given labels.
+ *
+ * @param labels a list of labels.
+ * @param map a map from LabelNodes to cloned LabelNodes.
+ * @return the clones of the given labels.
+ */
+ static LabelNode[] clone(final List labels, final Map map) {
+ LabelNode[] clones = new LabelNode[labels.size()];
+ for (int i = 0; i < clones.length; ++i) {
+ clones[i] = (LabelNode) map.get(labels.get(i));
+ }
+ return clones;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/AnnotationNode.java b/cglib-and-asm/src/org/mockito/asm/tree/AnnotationNode.java
new file mode 100644
index 0000000..092c0a5
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/AnnotationNode.java
@@ -0,0 +1,191 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.mockito.asm.AnnotationVisitor;
+
+/**
+ * A node that represents an annotationn.
+ *
+ * @author Eric Bruneton
+ */
+public class AnnotationNode implements AnnotationVisitor {
+
+ /**
+ * The class descriptor of the annotation class.
+ */
+ public String desc;
+
+ /**
+ * The name value pairs of this annotation. Each name value pair is stored
+ * as two consecutive elements in the list. The name is a {@link String},
+ * and the value may be a {@link Byte}, {@link Boolean}, {@link Character},
+ * {@link Short}, {@link Integer}, {@link Long}, {@link Float},
+ * {@link Double}, {@link String} or {@link org.mockito.asm.Type}, or an
+ * two elements String array (for enumeration values), a
+ * {@link AnnotationNode}, or a {@link List} of values of one of the
+ * preceding types. The list may be <tt>null</tt> if there is no name
+ * value pair.
+ */
+ public List values;
+
+ /**
+ * Constructs a new {@link AnnotationNode}.
+ *
+ * @param desc the class descriptor of the annotation class.
+ */
+ public AnnotationNode(final String desc) {
+ this.desc = desc;
+ }
+
+ /**
+ * Constructs a new {@link AnnotationNode} to visit an array value.
+ *
+ * @param values where the visited values must be stored.
+ */
+ AnnotationNode(final List values) {
+ this.values = values;
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the AnnotationVisitor interface
+ // ------------------------------------------------------------------------
+
+ public void visit(final String name, final Object value) {
+ if (values == null) {
+ values = new ArrayList(this.desc != null ? 2 : 1);
+ }
+ if (this.desc != null) {
+ values.add(name);
+ }
+ values.add(value);
+ }
+
+ public void visitEnum(
+ final String name,
+ final String desc,
+ final String value)
+ {
+ if (values == null) {
+ values = new ArrayList(this.desc != null ? 2 : 1);
+ }
+ if (this.desc != null) {
+ values.add(name);
+ }
+ values.add(new String[] { desc, value });
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String name,
+ final String desc)
+ {
+ if (values == null) {
+ values = new ArrayList(this.desc != null ? 2 : 1);
+ }
+ if (this.desc != null) {
+ values.add(name);
+ }
+ AnnotationNode annotation = new AnnotationNode(desc);
+ values.add(annotation);
+ return annotation;
+ }
+
+ public AnnotationVisitor visitArray(final String name) {
+ if (values == null) {
+ values = new ArrayList(this.desc != null ? 2 : 1);
+ }
+ if (this.desc != null) {
+ values.add(name);
+ }
+ List array = new ArrayList();
+ values.add(array);
+ return new AnnotationNode(array);
+ }
+
+ public void visitEnd() {
+ }
+
+ // ------------------------------------------------------------------------
+ // Accept methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Makes the given visitor visit this annotation.
+ *
+ * @param av an annotation visitor. Maybe <tt>null</tt>.
+ */
+ public void accept(final AnnotationVisitor av) {
+ if (av != null) {
+ if (values != null) {
+ for (int i = 0; i < values.size(); i += 2) {
+ String name = (String) values.get(i);
+ Object value = values.get(i + 1);
+ accept(av, name, value);
+ }
+ }
+ av.visitEnd();
+ }
+ }
+
+ /**
+ * Makes the given visitor visit a given annotation value.
+ *
+ * @param av an annotation visitor. Maybe <tt>null</tt>.
+ * @param name the value name.
+ * @param value the actual value.
+ */
+ static void accept(
+ final AnnotationVisitor av,
+ final String name,
+ final Object value)
+ {
+ if (av != null) {
+ if (value instanceof String[]) {
+ String[] typeconst = (String[]) value;
+ av.visitEnum(name, typeconst[0], typeconst[1]);
+ } else if (value instanceof AnnotationNode) {
+ AnnotationNode an = (AnnotationNode) value;
+ an.accept(av.visitAnnotation(name, an.desc));
+ } else if (value instanceof List) {
+ AnnotationVisitor v = av.visitArray(name);
+ List array = (List) value;
+ for (int j = 0; j < array.size(); ++j) {
+ accept(v, null, array.get(j));
+ }
+ v.visitEnd();
+ } else {
+ av.visit(name, value);
+ }
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/ClassNode.java b/cglib-and-asm/src/org/mockito/asm/tree/ClassNode.java
new file mode 100644
index 0000000..452d8ef
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/ClassNode.java
@@ -0,0 +1,280 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.FieldVisitor;
+import org.mockito.asm.MethodVisitor;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A node that represents a class.
+ *
+ * @author Eric Bruneton
+ */
+public class ClassNode extends MemberNode implements ClassVisitor {
+
+ /**
+ * The class version.
+ */
+ public int version;
+
+ /**
+ * The class's access flags (see {@link org.mockito.asm.Opcodes}). This
+ * field also indicates if the class is deprecated.
+ */
+ public int access;
+
+ /**
+ * The internal name of the class (see
+ * {@link org.mockito.asm.Type#getInternalName() getInternalName}).
+ */
+ public String name;
+
+ /**
+ * The signature of the class. Mayt be <tt>null</tt>.
+ */
+ public String signature;
+
+ /**
+ * The internal of name of the super class (see
+ * {@link org.mockito.asm.Type#getInternalName() getInternalName}). For
+ * interfaces, the super class is {@link Object}. May be <tt>null</tt>,
+ * but only for the {@link Object} class.
+ */
+ public String superName;
+
+ /**
+ * The internal names of the class's interfaces (see
+ * {@link org.mockito.asm.Type#getInternalName() getInternalName}). This
+ * list is a list of {@link String} objects.
+ */
+ public List interfaces;
+
+ /**
+ * The name of the source file from which this class was compiled. May be
+ * <tt>null</tt>.
+ */
+ public String sourceFile;
+
+ /**
+ * Debug information to compute the correspondance between source and
+ * compiled elements of the class. May be <tt>null</tt>.
+ */
+ public String sourceDebug;
+
+ /**
+ * The internal name of the enclosing class of the class. May be
+ * <tt>null</tt>.
+ */
+ public String outerClass;
+
+ /**
+ * The name of the method that contains the class, or <tt>null</tt> if the
+ * class is not enclosed in a method.
+ */
+ public String outerMethod;
+
+ /**
+ * The descriptor of the method that contains the class, or <tt>null</tt>
+ * if the class is not enclosed in a method.
+ */
+ public String outerMethodDesc;
+
+ /**
+ * Informations about the inner classes of this class. This list is a list
+ * of {@link InnerClassNode} objects.
+ *
+ * @associates org.mockito.asm.tree.InnerClassNode
+ */
+ public List innerClasses;
+
+ /**
+ * The fields of this class. This list is a list of {@link FieldNode}
+ * objects.
+ *
+ * @associates org.mockito.asm.tree.FieldNode
+ */
+ public List fields;
+
+ /**
+ * The methods of this class. This list is a list of {@link MethodNode}
+ * objects.
+ *
+ * @associates org.mockito.asm.tree.MethodNode
+ */
+ public List methods;
+
+ /**
+ * Constructs a new {@link ClassNode}.
+ */
+ public ClassNode() {
+ this.interfaces = new ArrayList();
+ this.innerClasses = new ArrayList();
+ this.fields = new ArrayList();
+ this.methods = new ArrayList();
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the ClassVisitor interface
+ // ------------------------------------------------------------------------
+
+ public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces)
+ {
+ this.version = version;
+ this.access = access;
+ this.name = name;
+ this.signature = signature;
+ this.superName = superName;
+ if (interfaces != null) {
+ this.interfaces.addAll(Arrays.asList(interfaces));
+ }
+ }
+
+ public void visitSource(final String file, final String debug) {
+ sourceFile = file;
+ sourceDebug = debug;
+ }
+
+ public void visitOuterClass(
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ outerClass = owner;
+ outerMethod = name;
+ outerMethodDesc = desc;
+ }
+
+ public void visitInnerClass(
+ final String name,
+ final String outerName,
+ final String innerName,
+ final int access)
+ {
+ InnerClassNode icn = new InnerClassNode(name,
+ outerName,
+ innerName,
+ access);
+ innerClasses.add(icn);
+ }
+
+ public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value)
+ {
+ FieldNode fn = new FieldNode(access, name, desc, signature, value);
+ fields.add(fn);
+ return fn;
+ }
+
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions)
+ {
+ MethodNode mn = new MethodNode(access,
+ name,
+ desc,
+ signature,
+ exceptions);
+ methods.add(mn);
+ return mn;
+ }
+
+ // ------------------------------------------------------------------------
+ // Accept method
+ // ------------------------------------------------------------------------
+
+ /**
+ * Makes the given class visitor visit this class.
+ *
+ * @param cv a class visitor.
+ */
+ public void accept(final ClassVisitor cv) {
+ // visits header
+ String[] interfaces = new String[this.interfaces.size()];
+ this.interfaces.toArray(interfaces);
+ cv.visit(version, access, name, signature, superName, interfaces);
+ // visits source
+ if (sourceFile != null || sourceDebug != null) {
+ cv.visitSource(sourceFile, sourceDebug);
+ }
+ // visits outer class
+ if (outerClass != null) {
+ cv.visitOuterClass(outerClass, outerMethod, outerMethodDesc);
+ }
+ // visits attributes
+ int i, n;
+ n = visibleAnnotations == null ? 0 : visibleAnnotations.size();
+ for (i = 0; i < n; ++i) {
+ AnnotationNode an = (AnnotationNode) visibleAnnotations.get(i);
+ an.accept(cv.visitAnnotation(an.desc, true));
+ }
+ n = invisibleAnnotations == null ? 0 : invisibleAnnotations.size();
+ for (i = 0; i < n; ++i) {
+ AnnotationNode an = (AnnotationNode) invisibleAnnotations.get(i);
+ an.accept(cv.visitAnnotation(an.desc, false));
+ }
+ n = attrs == null ? 0 : attrs.size();
+ for (i = 0; i < n; ++i) {
+ cv.visitAttribute((Attribute) attrs.get(i));
+ }
+ // visits inner classes
+ for (i = 0; i < innerClasses.size(); ++i) {
+ ((InnerClassNode) innerClasses.get(i)).accept(cv);
+ }
+ // visits fields
+ for (i = 0; i < fields.size(); ++i) {
+ ((FieldNode) fields.get(i)).accept(cv);
+ }
+ // visits methods
+ for (i = 0; i < methods.size(); ++i) {
+ ((MethodNode) methods.get(i)).accept(cv);
+ }
+ // visits end
+ cv.visitEnd();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/FieldInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/FieldInsnNode.java
new file mode 100644
index 0000000..cec597f
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/FieldInsnNode.java
@@ -0,0 +1,103 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A node that represents a field instruction. A field instruction is an
+ * instruction that loads or stores the value of a field of an object.
+ *
+ * @author Eric Bruneton
+ */
+public class FieldInsnNode extends AbstractInsnNode {
+
+ /**
+ * The internal name of the field's owner class (see
+ * {@link org.mockito.asm.Type#getInternalName() getInternalName}).
+ */
+ public String owner;
+
+ /**
+ * The field's name.
+ */
+ public String name;
+
+ /**
+ * The field's descriptor (see {@link org.mockito.asm.Type}).
+ */
+ public String desc;
+
+ /**
+ * Constructs a new {@link FieldInsnNode}.
+ *
+ * @param opcode the opcode of the type instruction to be constructed. This
+ * opcode must be GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
+ * @param owner the internal name of the field's owner class (see
+ * {@link org.mockito.asm.Type#getInternalName() getInternalName}).
+ * @param name the field's name.
+ * @param desc the field's descriptor (see {@link org.mockito.asm.Type}).
+ */
+ public FieldInsnNode(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ super(opcode);
+ this.owner = owner;
+ this.name = name;
+ this.desc = desc;
+ }
+
+ /**
+ * Sets the opcode of this instruction.
+ *
+ * @param opcode the new instruction opcode. This opcode must be GETSTATIC,
+ * PUTSTATIC, GETFIELD or PUTFIELD.
+ */
+ public void setOpcode(final int opcode) {
+ this.opcode = opcode;
+ }
+
+ public int getType() {
+ return FIELD_INSN;
+ }
+
+ public void accept(final MethodVisitor cv) {
+ cv.visitFieldInsn(opcode, owner, name, desc);
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new FieldInsnNode(opcode, owner, name, desc);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/FieldNode.java b/cglib-and-asm/src/org/mockito/asm/tree/FieldNode.java
new file mode 100644
index 0000000..4414f2f
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/FieldNode.java
@@ -0,0 +1,127 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.FieldVisitor;
+
+/**
+ * A node that represents a field.
+ *
+ * @author Eric Bruneton
+ */
+public class FieldNode extends MemberNode implements FieldVisitor {
+
+ /**
+ * The field's access flags (see {@link org.mockito.asm.Opcodes}). This
+ * field also indicates if the field is synthetic and/or deprecated.
+ */
+ public int access;
+
+ /**
+ * The field's name.
+ */
+ public String name;
+
+ /**
+ * The field's descriptor (see {@link org.mockito.asm.Type}).
+ */
+ public String desc;
+
+ /**
+ * The field's signature. May be <tt>null</tt>.
+ */
+ public String signature;
+
+ /**
+ * The field's initial value. This field, which may be <tt>null</tt> if
+ * the field does not have an initial value, must be an {@link Integer}, a
+ * {@link Float}, a {@link Long}, a {@link Double} or a {@link String}.
+ */
+ public Object value;
+
+ /**
+ * Constructs a new {@link FieldNode}.
+ *
+ * @param access the field's access flags (see
+ * {@link org.mockito.asm.Opcodes}). This parameter also indicates
+ * if the field is synthetic and/or deprecated.
+ * @param name the field's name.
+ * @param desc the field's descriptor (see
+ * {@link org.mockito.asm.Type Type}).
+ * @param signature the field's signature.
+ * @param value the field's initial value. This parameter, which may be
+ * <tt>null</tt> if the field does not have an initial value, must
+ * be an {@link Integer}, a {@link Float}, a {@link Long}, a
+ * {@link Double} or a {@link String}.
+ */
+ public FieldNode(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value)
+ {
+ this.access = access;
+ this.name = name;
+ this.desc = desc;
+ this.signature = signature;
+ this.value = value;
+ }
+
+ /**
+ * Makes the given class visitor visit this field.
+ *
+ * @param cv a class visitor.
+ */
+ public void accept(final ClassVisitor cv) {
+ FieldVisitor fv = cv.visitField(access, name, desc, signature, value);
+ if (fv == null) {
+ return;
+ }
+ int i, n;
+ n = visibleAnnotations == null ? 0 : visibleAnnotations.size();
+ for (i = 0; i < n; ++i) {
+ AnnotationNode an = (AnnotationNode) visibleAnnotations.get(i);
+ an.accept(fv.visitAnnotation(an.desc, true));
+ }
+ n = invisibleAnnotations == null ? 0 : invisibleAnnotations.size();
+ for (i = 0; i < n; ++i) {
+ AnnotationNode an = (AnnotationNode) invisibleAnnotations.get(i);
+ an.accept(fv.visitAnnotation(an.desc, false));
+ }
+ n = attrs == null ? 0 : attrs.size();
+ for (i = 0; i < n; ++i) {
+ fv.visitAttribute((Attribute) attrs.get(i));
+ }
+ fv.visitEnd();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/FrameNode.java b/cglib-and-asm/src/org/mockito/asm/tree/FrameNode.java
new file mode 100644
index 0000000..603dbca
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/FrameNode.java
@@ -0,0 +1,208 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+
+/**
+ * A node that represents a stack map frame. These nodes are pseudo instruction
+ * nodes in order to be inserted in an instruction list. In fact these nodes
+ * must(*) be inserted <i>just before</i> any instruction node <b>i</b> that
+ * follows an unconditionnal branch instruction such as GOTO or THROW, that is
+ * the target of a jump instruction, or that starts an exception handler block.
+ * The stack map frame types must describe the values of the local variables and
+ * of the operand stack elements <i>just before</i> <b>i</b> is executed. <br>
+ * <br> (*) this is mandatory only for classes whose version is greater than or
+ * equal to {@link Opcodes#V1_6 V1_6}.
+ *
+ * @author Eric Bruneton
+ */
+public class FrameNode extends AbstractInsnNode {
+
+ /**
+ * The type of this frame. Must be {@link Opcodes#F_NEW} for expanded
+ * frames, or {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND},
+ * {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or
+ * {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed frames.
+ */
+ public int type;
+
+ /**
+ * The types of the local variables of this stack map frame. Elements of
+ * this list can be Integer, String or LabelNode objects (for primitive,
+ * reference and uninitialized types respectively - see
+ * {@link MethodVisitor}).
+ */
+ public List local;
+
+ /**
+ * The types of the operand stack elements of this stack map frame. Elements
+ * of this list can be Integer, String or LabelNode objects (for primitive,
+ * reference and uninitialized types respectively - see
+ * {@link MethodVisitor}).
+ */
+ public List stack;
+
+ private FrameNode() {
+ super(-1);
+ }
+
+ /**
+ * Constructs a new {@link FrameNode}.
+ *
+ * @param type the type of this frame. Must be {@link Opcodes#F_NEW} for
+ * expanded frames, or {@link Opcodes#F_FULL},
+ * {@link Opcodes#F_APPEND}, {@link Opcodes#F_CHOP},
+ * {@link Opcodes#F_SAME} or {@link Opcodes#F_APPEND},
+ * {@link Opcodes#F_SAME1} for compressed frames.
+ * @param nLocal number of local variables of this stack map frame.
+ * @param local the types of the local variables of this stack map frame.
+ * Elements of this list can be Integer, String or LabelNode objects
+ * (for primitive, reference and uninitialized types respectively -
+ * see {@link MethodVisitor}).
+ * @param nStack number of operand stack elements of this stack map frame.
+ * @param stack the types of the operand stack elements of this stack map
+ * frame. Elements of this list can be Integer, String or LabelNode
+ * objects (for primitive, reference and uninitialized types
+ * respectively - see {@link MethodVisitor}).
+ */
+ public FrameNode(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack)
+ {
+ super(-1);
+ this.type = type;
+ switch (type) {
+ case Opcodes.F_NEW:
+ case Opcodes.F_FULL:
+ this.local = asList(nLocal, local);
+ this.stack = asList(nStack, stack);
+ break;
+ case Opcodes.F_APPEND:
+ this.local = asList(nLocal, local);
+ break;
+ case Opcodes.F_CHOP:
+ this.local = asList(nLocal, local);
+ break;
+ case Opcodes.F_SAME:
+ break;
+ case Opcodes.F_SAME1:
+ this.stack = asList(1, stack);
+ break;
+ }
+ }
+
+ public int getType() {
+ return FRAME;
+ }
+
+ /**
+ * Makes the given visitor visit this stack map frame.
+ *
+ * @param mv a method visitor.
+ */
+ public void accept(final MethodVisitor mv) {
+ switch (type) {
+ case Opcodes.F_NEW:
+ case Opcodes.F_FULL:
+ mv.visitFrame(type,
+ local.size(),
+ asArray(local),
+ stack.size(),
+ asArray(stack));
+ break;
+ case Opcodes.F_APPEND:
+ mv.visitFrame(type, local.size(), asArray(local), 0, null);
+ break;
+ case Opcodes.F_CHOP:
+ mv.visitFrame(type, local.size(), asArray(local), 0, null);
+ break;
+ case Opcodes.F_SAME:
+ mv.visitFrame(type, 0, null, 0, null);
+ break;
+ case Opcodes.F_SAME1:
+ mv.visitFrame(type, 0, null, 1, asArray(stack));
+ break;
+ }
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ FrameNode clone = new FrameNode();
+ clone.type = type;
+ if (local != null) {
+ clone.local = new ArrayList();
+ for (int i = 0; i < local.size(); ++i) {
+ Object l = local.get(i);
+ if (l instanceof LabelNode) {
+ l = labels.get(l);
+ }
+ clone.local.add(l);
+ }
+ }
+ if (stack != null) {
+ clone.stack = new ArrayList();
+ for (int i = 0; i < stack.size(); ++i) {
+ Object s = stack.get(i);
+ if (s instanceof LabelNode) {
+ s = labels.get(s);
+ }
+ clone.stack.add(s);
+ }
+ }
+ return clone;
+ }
+
+ // ------------------------------------------------------------------------
+
+ private static List asList(final int n, final Object[] o) {
+ return Arrays.asList(o).subList(0, n);
+ }
+
+ private static Object[] asArray(final List l) {
+ Object[] objs = new Object[l.size()];
+ for (int i = 0; i < objs.length; ++i) {
+ Object o = l.get(i);
+ if (o instanceof LabelNode) {
+ o = ((LabelNode) o).getLabel();
+ }
+ objs[i] = o;
+ }
+ return objs;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/IincInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/IincInsnNode.java
new file mode 100644
index 0000000..f9989c0
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/IincInsnNode.java
@@ -0,0 +1,77 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+
+/**
+ * A node that represents an IINC instruction.
+ *
+ * @author Eric Bruneton
+ */
+public class IincInsnNode extends AbstractInsnNode {
+
+ /**
+ * Index of the local variable to be incremented.
+ */
+ public int var;
+
+ /**
+ * Amount to increment the local variable by.
+ */
+ public int incr;
+
+ /**
+ * Constructs a new {@link IincInsnNode}.
+ *
+ * @param var index of the local variable to be incremented.
+ * @param incr increment amount to increment the local variable by.
+ */
+ public IincInsnNode(final int var, final int incr) {
+ super(Opcodes.IINC);
+ this.var = var;
+ this.incr = incr;
+ }
+
+ public int getType() {
+ return IINC_INSN;
+ }
+
+ public void accept(final MethodVisitor mv) {
+ mv.visitIincInsn(var, incr);
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new IincInsnNode(var, incr);
+ }
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/InnerClassNode.java b/cglib-and-asm/src/org/mockito/asm/tree/InnerClassNode.java
new file mode 100644
index 0000000..10eb9a4
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/InnerClassNode.java
@@ -0,0 +1,101 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import org.mockito.asm.ClassVisitor;
+
+/**
+ * A node that represents an inner class.
+ *
+ * @author Eric Bruneton
+ */
+public class InnerClassNode {
+
+ /**
+ * The internal name of an inner class (see
+ * {@link org.mockito.asm.Type#getInternalName() getInternalName}).
+ */
+ public String name;
+
+ /**
+ * The internal name of the class to which the inner class belongs (see
+ * {@link org.mockito.asm.Type#getInternalName() getInternalName}). May
+ * be <tt>null</tt>.
+ */
+ public String outerName;
+
+ /**
+ * The (simple) name of the inner class inside its enclosing class. May be
+ * <tt>null</tt> for anonymous inner classes.
+ */
+ public String innerName;
+
+ /**
+ * The access flags of the inner class as originally declared in the
+ * enclosing class.
+ */
+ public int access;
+
+ /**
+ * Constructs a new {@link InnerClassNode}.
+ *
+ * @param name the internal name of an inner class (see
+ * {@link org.mockito.asm.Type#getInternalName() getInternalName}).
+ * @param outerName the internal name of the class to which the inner class
+ * belongs (see
+ * {@link org.mockito.asm.Type#getInternalName() getInternalName}).
+ * May be <tt>null</tt>.
+ * @param innerName the (simple) name of the inner class inside its
+ * enclosing class. May be <tt>null</tt> for anonymous inner
+ * classes.
+ * @param access the access flags of the inner class as originally declared
+ * in the enclosing class.
+ */
+ public InnerClassNode(
+ final String name,
+ final String outerName,
+ final String innerName,
+ final int access)
+ {
+ this.name = name;
+ this.outerName = outerName;
+ this.innerName = innerName;
+ this.access = access;
+ }
+
+ /**
+ * Makes the given class visitor visit this inner class.
+ *
+ * @param cv a class visitor.
+ */
+ public void accept(final ClassVisitor cv) {
+ cv.visitInnerClass(name, outerName, innerName, access);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/InsnList.java b/cglib-and-asm/src/org/mockito/asm/tree/InsnList.java
new file mode 100644
index 0000000..bd81c0f
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/InsnList.java
@@ -0,0 +1,640 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A doubly linked list of {@link AbstractInsnNode} objects. <i>This
+ * implementation is not thread safe</i>.
+ */
+public class InsnList {
+
+ /**
+ * Indicates if preconditions of methods of this class must be checked.
+ * <i>Checking preconditions causes the {@link #indexOf indexOf},
+ * {@link #set set}, {@link #insert(AbstractInsnNode, AbstractInsnNode)},
+ * {@link #insert(AbstractInsnNode, InsnList)}, {@link #remove remove} and
+ * {@link #clear} methods to execute in O(n) time instead of O(1)</i>.
+ */
+ public static boolean check;
+
+ /**
+ * The number of instructions in this list.
+ */
+ private int size;
+
+ /**
+ * The first instruction in this list. May be <tt>null</tt>.
+ */
+ private AbstractInsnNode first;
+
+ /**
+ * The last instruction in this list. May be <tt>null</tt>.
+ */
+ private AbstractInsnNode last;
+
+ /**
+ * A cache of the instructions of this list. This cache is used to improve
+ * the performance of the {@link #get} method.
+ */
+ private AbstractInsnNode[] cache;
+
+ /**
+ * Returns the number of instructions in this list.
+ *
+ * @return the number of instructions in this list.
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Returns the first instruction in this list.
+ *
+ * @return the first instruction in this list, or <tt>null</tt> if the
+ * list is empty.
+ */
+ public AbstractInsnNode getFirst() {
+ return first;
+ }
+
+ /**
+ * Returns the last instruction in this list.
+ *
+ * @return the last instruction in this list, or <tt>null</tt> if the list
+ * is empty.
+ */
+ public AbstractInsnNode getLast() {
+ return last;
+ }
+
+ /**
+ * Returns the instruction whose index is given. This method builds a cache
+ * of the instructions in this list to avoid scanning the whole list each
+ * time it is called. Once the cache is built, this method run in constant
+ * time. This cache is invalidated by all the methods that modify the list.
+ *
+ * @param index the index of the instruction that must be returned.
+ * @return the instruction whose index is given.
+ * @throws IndexOutOfBoundsException if (index < 0 || index >= size()).
+ */
+ public AbstractInsnNode get(final int index) {
+ if (index < 0 || index >= size) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (cache == null) {
+ cache = toArray();
+ }
+ return cache[index];
+ }
+
+ /**
+ * Returns <tt>true</tt> if the given instruction belongs to this list.
+ * This method always scans the instructions of this list until it finds the
+ * given instruction or reaches the end of the list.
+ *
+ * @param insn an instruction.
+ * @return <tt>true</tt> if the given instruction belongs to this list.
+ */
+ public boolean contains(final AbstractInsnNode insn) {
+ AbstractInsnNode i = first;
+ while (i != null && i != insn) {
+ i = i.next;
+ }
+ return i != null;
+ }
+
+ /**
+ * Returns the index of the given instruction in this list. This method
+ * builds a cache of the instruction indexes to avoid scanning the whole
+ * list each time it is called. Once the cache is built, this method run in
+ * constant time. The cache is invalidated by all the methods that modify
+ * the list.
+ *
+ * @param insn an instruction <i>of this list</i>.
+ * @return the index of the given instruction in this list. <i>The result of
+ * this method is undefined if the given instruction does not belong
+ * to this list</i>. Use {@link #contains contains} to test if an
+ * instruction belongs to an instruction list or not.
+ * @throws IllegalArgumentException if {@link #check} is <tt>true</tt> and
+ * if insn does not belong to this list.
+ */
+ public int indexOf(final AbstractInsnNode insn) {
+ if (check && !contains(insn)) {
+ throw new IllegalArgumentException();
+ }
+ if (cache == null) {
+ cache = toArray();
+ }
+ return insn.index;
+ }
+
+ /**
+ * Makes the given visitor visit all of the instructions in this list.
+ *
+ * @param mv the method visitor that must visit the instructions.
+ */
+ public void accept(final MethodVisitor mv) {
+ AbstractInsnNode insn = first;
+ while (insn != null) {
+ insn.accept(mv);
+ insn = insn.next;
+ }
+ }
+
+ /**
+ * Returns an iterator over the instructions in this list.
+ *
+ * @return an iterator over the instructions in this list.
+ */
+ public ListIterator iterator() {
+ return iterator(0);
+ }
+
+ /**
+ * Returns an iterator over the instructions in this list.
+ *
+ * @return an iterator over the instructions in this list.
+ */
+ public ListIterator iterator(int index) {
+ return new InsnListIterator(index);
+ }
+
+ /**
+ * Returns an array containing all of the instructions in this list.
+ *
+ * @return an array containing all of the instructions in this list.
+ */
+ public AbstractInsnNode[] toArray() {
+ int i = 0;
+ AbstractInsnNode elem = first;
+ AbstractInsnNode[] insns = new AbstractInsnNode[size];
+ while (elem != null) {
+ insns[i] = elem;
+ elem.index = i++;
+ elem = elem.next;
+ }
+ return insns;
+ }
+
+ /**
+ * Replaces an instruction of this list with another instruction.
+ *
+ * @param location an instruction <i>of this list</i>.
+ * @param insn another instruction, <i>which must not belong to any
+ * {@link InsnList}</i>.
+ * @throws IllegalArgumentException if {@link #check} is <tt>true</tt>,
+ * and if i does not belong to this list or if insn belongs to an
+ * instruction list.
+ */
+ public void set(final AbstractInsnNode location, final AbstractInsnNode insn) {
+ if (check && !(contains(location) && insn.index == -1)) {
+ throw new IllegalArgumentException();
+ }
+ AbstractInsnNode next = location.next;
+ insn.next = next;
+ if (next != null) {
+ next.prev = insn;
+ } else {
+ last = insn;
+ }
+ AbstractInsnNode prev = location.prev;
+ insn.prev = prev;
+ if (prev != null) {
+ prev.next = insn;
+ } else {
+ first = insn;
+ }
+ if (cache != null) {
+ int index = location.index;
+ cache[index] = insn;
+ insn.index = index;
+ } else {
+ insn.index = 0; // insn now belongs to an InsnList
+ }
+ location.index = -1; // i no longer belongs to an InsnList
+ location.prev = null;
+ location.next = null;
+ }
+
+ /**
+ * Adds the given instruction to the end of this list.
+ *
+ * @param insn an instruction, <i>which must not belong to any
+ * {@link InsnList}</i>.
+ * @throws IllegalArgumentException if {@link #check} is <tt>true</tt>,
+ * and if insn belongs to an instruction list.
+ */
+ public void add(final AbstractInsnNode insn) {
+ if (check && insn.index != -1) {
+ throw new IllegalArgumentException();
+ }
+ ++size;
+ if (last == null) {
+ first = insn;
+ last = insn;
+ } else {
+ last.next = insn;
+ insn.prev = last;
+ }
+ last = insn;
+ cache = null;
+ insn.index = 0; // insn now belongs to an InsnList
+ }
+
+ /**
+ * Adds the given instructions to the end of this list.
+ *
+ * @param insns an instruction list, which is cleared during the process.
+ * @throws IllegalArgumentException if {@link #check} is <tt>true</tt>,
+ * and if insn == this.
+ */
+ public void add(final InsnList insns) {
+ if (check && insns == this) {
+ throw new IllegalArgumentException();
+ }
+ if (insns.size == 0) {
+ return;
+ }
+ size += insns.size;
+ if (last == null) {
+ first = insns.first;
+ last = insns.last;
+ } else {
+ AbstractInsnNode elem = insns.first;
+ last.next = elem;
+ elem.prev = last;
+ last = insns.last;
+ }
+ cache = null;
+ insns.removeAll(false);
+ }
+
+ /**
+ * Inserts the given instruction at the begining of this list.
+ *
+ * @param insn an instruction, <i>which must not belong to any
+ * {@link InsnList}</i>.
+ * @throws IllegalArgumentException if {@link #check} is <tt>true</tt>,
+ * and if insn belongs to an instruction list.
+ */
+ public void insert(final AbstractInsnNode insn) {
+ if (check && insn.index != -1) {
+ throw new IllegalArgumentException();
+ }
+ ++size;
+ if (first == null) {
+ first = insn;
+ last = insn;
+ } else {
+ first.prev = insn;
+ insn.next = first;
+ }
+ first = insn;
+ cache = null;
+ insn.index = 0; // insn now belongs to an InsnList
+ }
+
+ /**
+ * Inserts the given instructions at the begining of this list.
+ *
+ * @param insns an instruction list, which is cleared during the process.
+ * @throws IllegalArgumentException if {@link #check} is <tt>true</tt>,
+ * and if insn == this.
+ */
+ public void insert(final InsnList insns) {
+ if (check && insns == this) {
+ throw new IllegalArgumentException();
+ }
+ if (insns.size == 0) {
+ return;
+ }
+ size += insns.size;
+ if (first == null) {
+ first = insns.first;
+ last = insns.last;
+ } else {
+ AbstractInsnNode elem = insns.last;
+ first.prev = elem;
+ elem.next = first;
+ first = insns.first;
+ }
+ cache = null;
+ insns.removeAll(false);
+ }
+
+ /**
+ * Inserts the given instruction after the specified instruction.
+ *
+ * @param location an instruction <i>of this list</i> after which insn must be
+ * inserted.
+ * @param insn the instruction to be inserted, <i>which must not belong to
+ * any {@link InsnList}</i>.
+ * @throws IllegalArgumentException if {@link #check} is <tt>true</tt>,
+ * and if i does not belong to this list or if insn belongs to an
+ * instruction list.
+ */
+ public void insert(final AbstractInsnNode location, final AbstractInsnNode insn) {
+ if (check && !(contains(location) && insn.index == -1)) {
+ throw new IllegalArgumentException();
+ }
+ ++size;
+ AbstractInsnNode next = location.next;
+ if (next == null) {
+ last = insn;
+ } else {
+ next.prev = insn;
+ }
+ location.next = insn;
+ insn.next = next;
+ insn.prev = location;
+ cache = null;
+ insn.index = 0; // insn now belongs to an InsnList
+ }
+
+ /**
+ * Inserts the given instructions after the specified instruction.
+ *
+ * @param location an instruction <i>of this list</i> after which the instructions
+ * must be inserted.
+ * @param insns the instruction list to be inserted, which is cleared during
+ * the process.
+ * @throws IllegalArgumentException if {@link #check} is <tt>true</tt>,
+ * and if i does not belong to this list or if insns == this.
+ */
+ public void insert(final AbstractInsnNode location, final InsnList insns) {
+ if (check && !(contains(location) && insns != this)) {
+ throw new IllegalArgumentException();
+ }
+ if (insns.size == 0) {
+ return;
+ }
+ size += insns.size;
+ AbstractInsnNode ifirst = insns.first;
+ AbstractInsnNode ilast = insns.last;
+ AbstractInsnNode next = location.next;
+ if (next == null) {
+ last = ilast;
+ } else {
+ next.prev = ilast;
+ }
+ location.next = ifirst;
+ ilast.next = next;
+ ifirst.prev = location;
+ cache = null;
+ insns.removeAll(false);
+ }
+
+ /**
+ * Inserts the given instruction before the specified instruction.
+ *
+ * @param location an instruction <i>of this list</i> before which insn must be
+ * inserted.
+ * @param insn the instruction to be inserted, <i>which must not belong to
+ * any {@link InsnList}</i>.
+ * @throws IllegalArgumentException if {@link #check} is <tt>true</tt>,
+ * and if i does not belong to this list or if insn belongs to an
+ * instruction list.
+ */
+ public void insertBefore(final AbstractInsnNode location, final AbstractInsnNode insn) {
+ if (check && !(contains(location) && insn.index == -1)) {
+ throw new IllegalArgumentException();
+ }
+ ++size;
+ AbstractInsnNode prev = location.prev;
+ if (prev == null) {
+ first = insn;
+ } else {
+ prev.next = insn;
+ }
+ location.prev = insn;
+ insn.next = location;
+ insn.prev = prev;
+ cache = null;
+ insn.index = 0; // insn now belongs to an InsnList
+ }
+
+ /**
+ * Inserts the given instructions before the specified instruction.
+ *
+ * @param location an instruction <i>of this list</i> before which the instructions
+ * must be inserted.
+ * @param insns the instruction list to be inserted, which is cleared during
+ * the process.
+ * @throws IllegalArgumentException if {@link #check} is <tt>true</tt>,
+ * and if i does not belong to this list or if insns == this.
+ */
+ public void insertBefore(final AbstractInsnNode location, final InsnList insns) {
+ if (check && !(contains(location ) && insns != this)) {
+ throw new IllegalArgumentException();
+ }
+ if (insns.size == 0) {
+ return;
+ }
+ size += insns.size;
+ AbstractInsnNode ifirst = insns.first;
+ AbstractInsnNode ilast = insns.last;
+ AbstractInsnNode prev = location .prev;
+ if (prev == null) {
+ first = ifirst;
+ } else {
+ prev.next = ifirst;
+ }
+ location .prev = ilast;
+ ilast.next = location ;
+ ifirst.prev = prev;
+ cache = null;
+ insns.removeAll(false);
+ }
+
+
+
+ /**
+ * Removes the given instruction from this list.
+ *
+ * @param insn the instruction <i>of this list</i> that must be removed.
+ * @throws IllegalArgumentException if {@link #check} is <tt>true</tt>,
+ * and if insn does not belong to this list.
+ */
+ public void remove(final AbstractInsnNode insn) {
+ if (check && !contains(insn)) {
+ throw new IllegalArgumentException();
+ }
+ --size;
+ AbstractInsnNode next = insn.next;
+ AbstractInsnNode prev = insn.prev;
+ if (next == null) {
+ if (prev == null) {
+ first = null;
+ last = null;
+ } else {
+ prev.next = null;
+ last = prev;
+ }
+ } else {
+ if (prev == null) {
+ first = next;
+ next.prev = null;
+ } else {
+ prev.next = next;
+ next.prev = prev;
+ }
+ }
+ cache = null;
+ insn.index = -1; // insn no longer belongs to an InsnList
+ insn.prev = null;
+ insn.next = null;
+ }
+
+ /**
+ * Removes all of the instructions of this list.
+ *
+ * @param mark if the instructions must be marked as no longer belonging to
+ * any {@link InsnList}.
+ */
+ private void removeAll(final boolean mark) {
+ if (mark) {
+ AbstractInsnNode insn = first;
+ while (insn != null) {
+ AbstractInsnNode next = insn.next;
+ insn.index = -1; // insn no longer belongs to an InsnList
+ insn.prev = null;
+ insn.next = null;
+ insn = next;
+ }
+ }
+ size = 0;
+ first = null;
+ last = null;
+ cache = null;
+ }
+
+ /**
+ * Removes all of the instructions of this list.
+ */
+ public void clear() {
+ removeAll(check);
+ }
+
+ /**
+ * Reset all labels in the instruction list. This method should be called
+ * before reusing same instructions list between several
+ * <code>ClassWriter</code>s.
+ */
+ public void resetLabels() {
+ AbstractInsnNode insn = first;
+ while (insn != null) {
+ if (insn instanceof LabelNode) {
+ ((LabelNode) insn).resetLabel();
+ }
+ insn = insn.next;
+ }
+ }
+
+ private final class InsnListIterator implements ListIterator {
+ AbstractInsnNode next;
+ AbstractInsnNode prev;
+
+ private InsnListIterator(int index) {
+ if(index==size()) {
+ next = null;
+ prev = getLast();
+ } else {
+ next = get(index);
+ prev = next.prev;
+ }
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ public Object next() {
+ if (next == null) {
+ throw new NoSuchElementException();
+ }
+ AbstractInsnNode result = next;
+ prev = result;
+ next = result.next;
+ return result;
+ }
+
+ public void remove() {
+ InsnList.this.remove(prev);
+ prev = prev.prev;
+ }
+
+ public boolean hasPrevious() {
+ return prev != null;
+ }
+
+ public Object previous() {
+ AbstractInsnNode result = prev;
+ next = result;
+ prev = result.prev;
+ return result;
+ }
+
+ public int nextIndex() {
+ if (next == null) {
+ return size();
+ }
+ if (cache == null) {
+ cache = toArray();
+ }
+ return next.index;
+ }
+
+ public int previousIndex() {
+ if (prev == null) {
+ return -1;
+ }
+ if (cache == null) {
+ cache = toArray();
+ }
+ return prev.index;
+ }
+
+ public void add(Object o) {
+ InsnList.this.insertBefore(next, (AbstractInsnNode) o);
+ prev = (AbstractInsnNode) o;
+ }
+
+ public void set(Object o) {
+ InsnList.this.set(next.prev, (AbstractInsnNode) o);
+ prev = (AbstractInsnNode) o;
+ }
+ }
+
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/InsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/InsnNode.java
new file mode 100644
index 0000000..eda7eb2
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/InsnNode.java
@@ -0,0 +1,81 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A node that represents a zero operand instruction.
+ *
+ * @author Eric Bruneton
+ */
+public class InsnNode extends AbstractInsnNode {
+
+ /**
+ * Constructs a new {@link InsnNode}.
+ *
+ * @param opcode the opcode of the instruction to be constructed. This
+ * opcode must be NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1,
+ * ICONST_2, ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1,
+ * FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, LALOAD,
+ * FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE,
+ * FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2,
+ * DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, IADD, LADD,
+ * FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV,
+ * LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG,
+ * ISHL, LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR,
+ * LXOR, I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F,
+ * I2B, I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN,
+ * FRETURN, DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW,
+ * MONITORENTER, or MONITOREXIT.
+ */
+ public InsnNode(final int opcode) {
+ super(opcode);
+ }
+
+ public int getType() {
+ return INSN;
+ }
+
+ /**
+ * Makes the given visitor visit this instruction.
+ *
+ * @param mv a method visitor.
+ */
+ public void accept(final MethodVisitor mv) {
+ mv.visitInsn(opcode);
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new InsnNode(opcode);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/IntInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/IntInsnNode.java
new file mode 100644
index 0000000..8578583
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/IntInsnNode.java
@@ -0,0 +1,81 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A node that represents an instruction with a single int operand.
+ *
+ * @author Eric Bruneton
+ */
+public class IntInsnNode extends AbstractInsnNode {
+
+ /**
+ * The operand of this instruction.
+ */
+ public int operand;
+
+ /**
+ * Constructs a new {@link IntInsnNode}.
+ *
+ * @param opcode the opcode of the instruction to be constructed. This
+ * opcode must be BIPUSH, SIPUSH or NEWARRAY.
+ * @param operand the operand of the instruction to be constructed.
+ */
+ public IntInsnNode(final int opcode, final int operand) {
+ super(opcode);
+ this.operand = operand;
+ }
+
+ /**
+ * Sets the opcode of this instruction.
+ *
+ * @param opcode the new instruction opcode. This opcode must be BIPUSH,
+ * SIPUSH or NEWARRAY.
+ */
+ public void setOpcode(final int opcode) {
+ this.opcode = opcode;
+ }
+
+ public int getType() {
+ return INT_INSN;
+ }
+
+ public void accept(final MethodVisitor mv) {
+ mv.visitIntInsn(opcode, operand);
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new IntInsnNode(opcode, operand);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/JumpInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/JumpInsnNode.java
new file mode 100644
index 0000000..efc94a7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/JumpInsnNode.java
@@ -0,0 +1,89 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A node that represents a jump instruction. A jump instruction is an
+ * instruction that may jump to another instruction.
+ *
+ * @author Eric Bruneton
+ */
+public class JumpInsnNode extends AbstractInsnNode {
+
+ /**
+ * The operand of this instruction. This operand is a label that designates
+ * the instruction to which this instruction may jump.
+ */
+ public LabelNode label;
+
+ /**
+ * Constructs a new {@link JumpInsnNode}.
+ *
+ * @param opcode the opcode of the type instruction to be constructed. This
+ * opcode must be IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ,
+ * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ,
+ * IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL.
+ * @param label the operand of the instruction to be constructed. This
+ * operand is a label that designates the instruction to which the
+ * jump instruction may jump.
+ */
+ public JumpInsnNode(final int opcode, final LabelNode label) {
+ super(opcode);
+ this.label = label;
+ }
+
+ /**
+ * Sets the opcode of this instruction.
+ *
+ * @param opcode the new instruction opcode. This opcode must be IFEQ, IFNE,
+ * IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT,
+ * IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, GOTO, JSR,
+ * IFNULL or IFNONNULL.
+ */
+ public void setOpcode(final int opcode) {
+ this.opcode = opcode;
+ }
+
+ public int getType() {
+ return JUMP_INSN;
+ }
+
+ public void accept(final MethodVisitor mv) {
+ mv.visitJumpInsn(opcode, label.getLabel());
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new JumpInsnNode(opcode, clone(label, labels));
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/LabelNode.java b/cglib-and-asm/src/org/mockito/asm/tree/LabelNode.java
new file mode 100644
index 0000000..001f218
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/LabelNode.java
@@ -0,0 +1,75 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.Label;
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * An {@link AbstractInsnNode} that encapsulates a {@link Label}.
+ */
+public class LabelNode extends AbstractInsnNode {
+
+ private Label label;
+
+ public LabelNode() {
+ super(-1);
+ }
+
+ public LabelNode(final Label label) {
+ super(-1);
+ this.label = label;
+ }
+
+ public int getType() {
+ return LABEL;
+ }
+
+ public Label getLabel() {
+ if (label == null) {
+ label = new Label();
+ }
+ return label;
+ }
+
+ public void accept(final MethodVisitor cv) {
+ cv.visitLabel(getLabel());
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return (LabelNode) labels.get(this);
+ }
+
+ public void resetLabel() {
+ label = null;
+ }
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/LdcInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/LdcInsnNode.java
new file mode 100644
index 0000000..b960448
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/LdcInsnNode.java
@@ -0,0 +1,74 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+
+/**
+ * A node that represents an LDC instruction.
+ *
+ * @author Eric Bruneton
+ */
+public class LdcInsnNode extends AbstractInsnNode {
+
+ /**
+ * The constant to be loaded on the stack. This parameter must be a non null
+ * {@link Integer}, a {@link Float}, a {@link Long}, a {@link Double}, a
+ * {@link String} or a {@link org.mockito.asm.Type}.
+ */
+ public Object cst;
+
+ /**
+ * Constructs a new {@link LdcInsnNode}.
+ *
+ * @param cst the constant to be loaded on the stack. This parameter must be
+ * a non null {@link Integer}, a {@link Float}, a {@link Long}, a
+ * {@link Double} or a {@link String}.
+ */
+ public LdcInsnNode(final Object cst) {
+ super(Opcodes.LDC);
+ this.cst = cst;
+ }
+
+ public int getType() {
+ return LDC_INSN;
+ }
+
+ public void accept(final MethodVisitor mv) {
+ mv.visitLdcInsn(cst);
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new LdcInsnNode(cst);
+ }
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/LineNumberNode.java b/cglib-and-asm/src/org/mockito/asm/tree/LineNumberNode.java
new file mode 100644
index 0000000..8b1ac86
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/LineNumberNode.java
@@ -0,0 +1,79 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A node that represents a line number declaration. These nodes are pseudo
+ * instruction nodes in order to be inserted in an instruction list.
+ *
+ * @author Eric Bruneton
+ */
+public class LineNumberNode extends AbstractInsnNode {
+
+ /**
+ * A line number. This number refers to the source file from which the class
+ * was compiled.
+ */
+ public int line;
+
+ /**
+ * The first instruction corresponding to this line number.
+ */
+ public LabelNode start;
+
+ /**
+ * Constructs a new {@link LineNumberNode}.
+ *
+ * @param line a line number. This number refers to the source file from
+ * which the class was compiled.
+ * @param start the first instruction corresponding to this line number.
+ */
+ public LineNumberNode(final int line, final LabelNode start) {
+ super(-1);
+ this.line = line;
+ this.start = start;
+ }
+
+ public int getType() {
+ return LINE;
+ }
+
+ public void accept(final MethodVisitor mv) {
+ mv.visitLineNumber(line, start.getLabel());
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new LineNumberNode(line, clone(start, labels));
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/LocalVariableNode.java b/cglib-and-asm/src/org/mockito/asm/tree/LocalVariableNode.java
new file mode 100644
index 0000000..f45898e
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/LocalVariableNode.java
@@ -0,0 +1,115 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A node that represents a local variable declaration.
+ *
+ * @author Eric Bruneton
+ */
+public class LocalVariableNode {
+
+ /**
+ * The name of a local variable.
+ */
+ public String name;
+
+ /**
+ * The type descriptor of this local variable.
+ */
+ public String desc;
+
+ /**
+ * The signature of this local variable. May be <tt>null</tt>.
+ */
+ public String signature;
+
+ /**
+ * The first instruction corresponding to the scope of this local variable
+ * (inclusive).
+ */
+ public LabelNode start;
+
+ /**
+ * The last instruction corresponding to the scope of this local variable
+ * (exclusive).
+ */
+ public LabelNode end;
+
+ /**
+ * The local variable's index.
+ */
+ public int index;
+
+ /**
+ * Constructs a new {@link LocalVariableNode}.
+ *
+ * @param name the name of a local variable.
+ * @param desc the type descriptor of this local variable.
+ * @param signature the signature of this local variable. May be
+ * <tt>null</tt>.
+ * @param start the first instruction corresponding to the scope of this
+ * local variable (inclusive).
+ * @param end the last instruction corresponding to the scope of this local
+ * variable (exclusive).
+ * @param index the local variable's index.
+ */
+ public LocalVariableNode(
+ final String name,
+ final String desc,
+ final String signature,
+ final LabelNode start,
+ final LabelNode end,
+ final int index)
+ {
+ this.name = name;
+ this.desc = desc;
+ this.signature = signature;
+ this.start = start;
+ this.end = end;
+ this.index = index;
+ }
+
+ /**
+ * Makes the given visitor visit this local variable declaration.
+ *
+ * @param mv a method visitor.
+ */
+ public void accept(final MethodVisitor mv) {
+ mv.visitLocalVariable(name,
+ desc,
+ signature,
+ start.getLabel(),
+ end.getLabel(),
+ index);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/LookupSwitchInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/LookupSwitchInsnNode.java
new file mode 100644
index 0000000..809b764
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/LookupSwitchInsnNode.java
@@ -0,0 +1,113 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import org.mockito.asm.Label;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * A node that represents a LOOKUPSWITCH instruction.
+ *
+ * @author Eric Bruneton
+ */
+public class LookupSwitchInsnNode extends AbstractInsnNode {
+
+ /**
+ * Beginning of the default handler block.
+ */
+ public LabelNode dflt;
+
+ /**
+ * The values of the keys. This list is a list of {@link Integer} objects.
+ */
+ public List keys;
+
+ /**
+ * Beginnings of the handler blocks. This list is a list of
+ * {@link LabelNode} objects.
+ */
+ public List labels;
+
+ /**
+ * Constructs a new {@link LookupSwitchInsnNode}.
+ *
+ * @param dflt beginning of the default handler block.
+ * @param keys the values of the keys.
+ * @param labels beginnings of the handler blocks. <tt>labels[i]</tt> is
+ * the beginning of the handler block for the <tt>keys[i]</tt> key.
+ */
+ public LookupSwitchInsnNode(
+ final LabelNode dflt,
+ final int[] keys,
+ final LabelNode[] labels)
+ {
+ super(Opcodes.LOOKUPSWITCH);
+ this.dflt = dflt;
+ this.keys = new ArrayList(keys == null ? 0 : keys.length);
+ this.labels = new ArrayList(labels == null ? 0 : labels.length);
+ if (keys != null) {
+ for (int i = 0; i < keys.length; ++i) {
+ this.keys.add(new Integer(keys[i]));
+ }
+ }
+ if (labels != null) {
+ this.labels.addAll(Arrays.asList(labels));
+ }
+ }
+
+ public int getType() {
+ return LOOKUPSWITCH_INSN;
+ }
+
+ public void accept(final MethodVisitor mv) {
+ int[] keys = new int[this.keys.size()];
+ for (int i = 0; i < keys.length; ++i) {
+ keys[i] = ((Integer) this.keys.get(i)).intValue();
+ }
+ Label[] labels = new Label[this.labels.size()];
+ for (int i = 0; i < labels.length; ++i) {
+ labels[i] = ((LabelNode) this.labels.get(i)).getLabel();
+ }
+ mv.visitLookupSwitchInsn(dflt.getLabel(), keys, labels);
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ LookupSwitchInsnNode clone = new LookupSwitchInsnNode(clone(dflt,
+ labels), null, clone(this.labels, labels));
+ clone.keys.addAll(keys);
+ return clone;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/MemberNode.java b/cglib-and-asm/src/org/mockito/asm/tree/MemberNode.java
new file mode 100644
index 0000000..501efe7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/MemberNode.java
@@ -0,0 +1,120 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Attribute;
+
+/**
+ * An abstract class, field or method node.
+ *
+ * @author Eric Bruneton
+ */
+public abstract class MemberNode {
+
+ /**
+ * The runtime visible annotations of this class, field or method. This list
+ * is a list of {@link AnnotationNode} objects. May be <tt>null</tt>.
+ *
+ * @associates org.mockito.asm.tree.AnnotationNode
+ * @label visible
+ */
+ public List visibleAnnotations;
+
+ /**
+ * The runtime invisible annotations of this class, field or method. This
+ * list is a list of {@link AnnotationNode} objects. May be <tt>null</tt>.
+ *
+ * @associates org.mockito.asm.tree.AnnotationNode
+ * @label invisible
+ */
+ public List invisibleAnnotations;
+
+ /**
+ * The non standard attributes of this class, field or method. This list is
+ * a list of {@link Attribute} objects. May be <tt>null</tt>.
+ *
+ * @associates org.mockito.asm.Attribute
+ */
+ public List attrs;
+
+ /**
+ * Constructs a new {@link MemberNode}.
+ */
+ protected MemberNode() {
+ }
+
+ /**
+ * Visits an annotation of this class, field or method.
+ *
+ * @param desc the class descriptor of the annotation class.
+ * @param visible <tt>true</tt> if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values.
+ */
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ AnnotationNode an = new AnnotationNode(desc);
+ if (visible) {
+ if (visibleAnnotations == null) {
+ visibleAnnotations = new ArrayList(1);
+ }
+ visibleAnnotations.add(an);
+ } else {
+ if (invisibleAnnotations == null) {
+ invisibleAnnotations = new ArrayList(1);
+ }
+ invisibleAnnotations.add(an);
+ }
+ return an;
+ }
+
+ /**
+ * Visits a non standard attribute of this class, field or method.
+ *
+ * @param attr an attribute.
+ */
+ public void visitAttribute(final Attribute attr) {
+ if (attrs == null) {
+ attrs = new ArrayList(1);
+ }
+ attrs.add(attr);
+ }
+
+ /**
+ * Visits the end of this class, field or method.
+ */
+ public void visitEnd() {
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/MethodInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/MethodInsnNode.java
new file mode 100644
index 0000000..68b4499
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/MethodInsnNode.java
@@ -0,0 +1,104 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A node that represents a method instruction. A method instruction is an
+ * instruction that invokes a method.
+ *
+ * @author Eric Bruneton
+ */
+public class MethodInsnNode extends AbstractInsnNode {
+
+ /**
+ * The internal name of the method's owner class (see
+ * {@link org.mockito.asm.Type#getInternalName() getInternalName}).
+ */
+ public String owner;
+
+ /**
+ * The method's name.
+ */
+ public String name;
+
+ /**
+ * The method's descriptor (see {@link org.mockito.asm.Type}).
+ */
+ public String desc;
+
+ /**
+ * Constructs a new {@link MethodInsnNode}.
+ *
+ * @param opcode the opcode of the type instruction to be constructed. This
+ * opcode must be INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or
+ * INVOKEINTERFACE.
+ * @param owner the internal name of the method's owner class (see
+ * {@link org.mockito.asm.Type#getInternalName() getInternalName}).
+ * @param name the method's name.
+ * @param desc the method's descriptor (see {@link org.mockito.asm.Type}).
+ */
+ public MethodInsnNode(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ super(opcode);
+ this.owner = owner;
+ this.name = name;
+ this.desc = desc;
+ }
+
+ /**
+ * Sets the opcode of this instruction.
+ *
+ * @param opcode the new instruction opcode. This opcode must be
+ * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE.
+ */
+ public void setOpcode(final int opcode) {
+ this.opcode = opcode;
+ }
+
+ public int getType() {
+ return METHOD_INSN;
+ }
+
+ public void accept(final MethodVisitor mv) {
+ mv.visitMethodInsn(opcode, owner, name, desc);
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new MethodInsnNode(opcode, owner, name, desc);
+ }
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/MethodNode.java b/cglib-and-asm/src/org/mockito/asm/tree/MethodNode.java
new file mode 100644
index 0000000..11a17e8
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/MethodNode.java
@@ -0,0 +1,490 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Attribute;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Label;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+import org.mockito.asm.Type;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A node that represents a method.
+ *
+ * @author Eric Bruneton
+ */
+public class MethodNode extends MemberNode implements MethodVisitor {
+
+ /**
+ * The method's access flags (see {@link Opcodes}). This field also
+ * indicates if the method is synthetic and/or deprecated.
+ */
+ public int access;
+
+ /**
+ * The method's name.
+ */
+ public String name;
+
+ /**
+ * The method's descriptor (see {@link Type}).
+ */
+ public String desc;
+
+ /**
+ * The method's signature. May be <tt>null</tt>.
+ */
+ public String signature;
+
+ /**
+ * The internal names of the method's exception classes (see
+ * {@link Type#getInternalName() getInternalName}). This list is a list of
+ * {@link String} objects.
+ */
+ public List exceptions;
+
+ /**
+ * The default value of this annotation interface method. This field must be
+ * a {@link Byte}, {@link Boolean}, {@link Character}, {@link Short},
+ * {@link Integer}, {@link Long}, {@link Float}, {@link Double},
+ * {@link String} or {@link Type}, or an two elements String array (for
+ * enumeration values), a {@link AnnotationNode}, or a {@link List} of
+ * values of one of the preceding types. May be <tt>null</tt>.
+ */
+ public Object annotationDefault;
+
+ /**
+ * The runtime visible parameter annotations of this method. These lists are
+ * lists of {@link AnnotationNode} objects. May be <tt>null</tt>.
+ *
+ * @associates org.mockito.asm.tree.AnnotationNode
+ * @label invisible parameters
+ */
+ public List[] visibleParameterAnnotations;
+
+ /**
+ * The runtime invisible parameter annotations of this method. These lists
+ * are lists of {@link AnnotationNode} objects. May be <tt>null</tt>.
+ *
+ * @associates org.mockito.asm.tree.AnnotationNode
+ * @label visible parameters
+ */
+ public List[] invisibleParameterAnnotations;
+
+ /**
+ * The instructions of this method. This list is a list of
+ * {@link AbstractInsnNode} objects.
+ *
+ * @associates org.mockito.asm.tree.AbstractInsnNode
+ * @label instructions
+ */
+ public InsnList instructions;
+
+ /**
+ * The try catch blocks of this method. This list is a list of
+ * {@link TryCatchBlockNode} objects.
+ *
+ * @associates org.mockito.asm.tree.TryCatchBlockNode
+ */
+ public List tryCatchBlocks;
+
+ /**
+ * The maximum stack size of this method.
+ */
+ public int maxStack;
+
+ /**
+ * The maximum number of local variables of this method.
+ */
+ public int maxLocals;
+
+ /**
+ * The local variables of this method. This list is a list of
+ * {@link LocalVariableNode} objects. May be <tt>null</tt>
+ *
+ * @associates org.mockito.asm.tree.LocalVariableNode
+ */
+ public List localVariables;
+
+ /**
+ * Constructs an unitialized {@link MethodNode}.
+ */
+ public MethodNode() {
+ this.instructions = new InsnList();
+ }
+
+ /**
+ * Constructs a new {@link MethodNode}.
+ *
+ * @param access the method's access flags (see {@link Opcodes}). This
+ * parameter also indicates if the method is synthetic and/or
+ * deprecated.
+ * @param name the method's name.
+ * @param desc the method's descriptor (see {@link Type}).
+ * @param signature the method's signature. May be <tt>null</tt>.
+ * @param exceptions the internal names of the method's exception classes
+ * (see {@link Type#getInternalName() getInternalName}). May be
+ * <tt>null</tt>.
+ */
+ public MethodNode(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions)
+ {
+ this();
+ this.access = access;
+ this.name = name;
+ this.desc = desc;
+ this.signature = signature;
+ this.exceptions = new ArrayList(exceptions == null
+ ? 0
+ : exceptions.length);
+ boolean isAbstract = (access & Opcodes.ACC_ABSTRACT) != 0;
+ if (!isAbstract) {
+ this.localVariables = new ArrayList(5);
+ }
+ this.tryCatchBlocks = new ArrayList();
+ if (exceptions != null) {
+ this.exceptions.addAll(Arrays.asList(exceptions));
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the MethodVisitor interface
+ // ------------------------------------------------------------------------
+
+ public AnnotationVisitor visitAnnotationDefault() {
+ return new AnnotationNode(new ArrayList(0) {
+ public boolean add(final Object o) {
+ annotationDefault = o;
+ return super.add(o);
+ }
+ });
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(
+ final int parameter,
+ final String desc,
+ final boolean visible)
+ {
+ AnnotationNode an = new AnnotationNode(desc);
+ if (visible) {
+ if (visibleParameterAnnotations == null) {
+ int params = Type.getArgumentTypes(this.desc).length;
+ visibleParameterAnnotations = new List[params];
+ }
+ if (visibleParameterAnnotations[parameter] == null) {
+ visibleParameterAnnotations[parameter] = new ArrayList(1);
+ }
+ visibleParameterAnnotations[parameter].add(an);
+ } else {
+ if (invisibleParameterAnnotations == null) {
+ int params = Type.getArgumentTypes(this.desc).length;
+ invisibleParameterAnnotations = new List[params];
+ }
+ if (invisibleParameterAnnotations[parameter] == null) {
+ invisibleParameterAnnotations[parameter] = new ArrayList(1);
+ }
+ invisibleParameterAnnotations[parameter].add(an);
+ }
+ return an;
+ }
+
+ public void visitCode() {
+ }
+
+ public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack)
+ {
+ instructions.add(new FrameNode(type, nLocal, local == null
+ ? null
+ : getLabelNodes(local), nStack, stack == null
+ ? null
+ : getLabelNodes(stack)));
+ }
+
+ public void visitInsn(final int opcode) {
+ instructions.add(new InsnNode(opcode));
+ }
+
+ public void visitIntInsn(final int opcode, final int operand) {
+ instructions.add(new IntInsnNode(opcode, operand));
+ }
+
+ public void visitVarInsn(final int opcode, final int var) {
+ instructions.add(new VarInsnNode(opcode, var));
+ }
+
+ public void visitTypeInsn(final int opcode, final String type) {
+ instructions.add(new TypeInsnNode(opcode, type));
+ }
+
+ public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ instructions.add(new FieldInsnNode(opcode, owner, name, desc));
+ }
+
+ public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ instructions.add(new MethodInsnNode(opcode, owner, name, desc));
+ }
+
+ public void visitJumpInsn(final int opcode, final Label label) {
+ instructions.add(new JumpInsnNode(opcode, getLabelNode(label)));
+ }
+
+ public void visitLabel(final Label label) {
+ instructions.add(getLabelNode(label));
+ }
+
+ public void visitLdcInsn(final Object cst) {
+ instructions.add(new LdcInsnNode(cst));
+ }
+
+ public void visitIincInsn(final int var, final int increment) {
+ instructions.add(new IincInsnNode(var, increment));
+ }
+
+ public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label[] labels)
+ {
+ instructions.add(new TableSwitchInsnNode(min,
+ max,
+ getLabelNode(dflt),
+ getLabelNodes(labels)));
+ }
+
+ public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int[] keys,
+ final Label[] labels)
+ {
+ instructions.add(new LookupSwitchInsnNode(getLabelNode(dflt),
+ keys,
+ getLabelNodes(labels)));
+ }
+
+ public void visitMultiANewArrayInsn(final String desc, final int dims) {
+ instructions.add(new MultiANewArrayInsnNode(desc, dims));
+ }
+
+ public void visitTryCatchBlock(
+ final Label start,
+ final Label end,
+ final Label handler,
+ final String type)
+ {
+ tryCatchBlocks.add(new TryCatchBlockNode(getLabelNode(start),
+ getLabelNode(end),
+ getLabelNode(handler),
+ type));
+ }
+
+ public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index)
+ {
+ localVariables.add(new LocalVariableNode(name,
+ desc,
+ signature,
+ getLabelNode(start),
+ getLabelNode(end),
+ index));
+ }
+
+ public void visitLineNumber(final int line, final Label start) {
+ instructions.add(new LineNumberNode(line, getLabelNode(start)));
+ }
+
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ this.maxStack = maxStack;
+ this.maxLocals = maxLocals;
+ }
+
+ /**
+ * Returns the LabelNode corresponding to the given Label. Creates a new
+ * LabelNode if necessary. The default implementation of this method uses
+ * the {@link Label#info} field to store associations between labels and
+ * label nodes.
+ *
+ * @param l a Label.
+ * @return the LabelNode corresponding to l.
+ */
+ protected LabelNode getLabelNode(final Label l) {
+ if (!(l.info instanceof LabelNode)) {
+ l.info = new LabelNode(l);
+ }
+ return (LabelNode) l.info;
+ }
+
+ private LabelNode[] getLabelNodes(final Label[] l) {
+ LabelNode[] nodes = new LabelNode[l.length];
+ for (int i = 0; i < l.length; ++i) {
+ nodes[i] = getLabelNode(l[i]);
+ }
+ return nodes;
+ }
+
+ private Object[] getLabelNodes(final Object[] objs) {
+ Object[] nodes = new Object[objs.length];
+ for (int i = 0; i < objs.length; ++i) {
+ Object o = objs[i];
+ if (o instanceof Label) {
+ o = getLabelNode((Label) o);
+ }
+ nodes[i] = o;
+ }
+ return nodes;
+ }
+
+ // ------------------------------------------------------------------------
+ // Accept method
+ // ------------------------------------------------------------------------
+
+ /**
+ * Makes the given class visitor visit this method.
+ *
+ * @param cv a class visitor.
+ */
+ public void accept(final ClassVisitor cv) {
+ String[] exceptions = new String[this.exceptions.size()];
+ this.exceptions.toArray(exceptions);
+ MethodVisitor mv = cv.visitMethod(access,
+ name,
+ desc,
+ signature,
+ exceptions);
+ if (mv != null) {
+ accept(mv);
+ }
+ }
+
+ /**
+ * Makes the given method visitor visit this method.
+ *
+ * @param mv a method visitor.
+ */
+ public void accept(final MethodVisitor mv) {
+ // visits the method attributes
+ int i, j, n;
+ if (annotationDefault != null) {
+ AnnotationVisitor av = mv.visitAnnotationDefault();
+ AnnotationNode.accept(av, null, annotationDefault);
+ if (av != null) {
+ av.visitEnd();
+ }
+ }
+ n = visibleAnnotations == null ? 0 : visibleAnnotations.size();
+ for (i = 0; i < n; ++i) {
+ AnnotationNode an = (AnnotationNode) visibleAnnotations.get(i);
+ an.accept(mv.visitAnnotation(an.desc, true));
+ }
+ n = invisibleAnnotations == null ? 0 : invisibleAnnotations.size();
+ for (i = 0; i < n; ++i) {
+ AnnotationNode an = (AnnotationNode) invisibleAnnotations.get(i);
+ an.accept(mv.visitAnnotation(an.desc, false));
+ }
+ n = visibleParameterAnnotations == null
+ ? 0
+ : visibleParameterAnnotations.length;
+ for (i = 0; i < n; ++i) {
+ List l = visibleParameterAnnotations[i];
+ if (l == null) {
+ continue;
+ }
+ for (j = 0; j < l.size(); ++j) {
+ AnnotationNode an = (AnnotationNode) l.get(j);
+ an.accept(mv.visitParameterAnnotation(i, an.desc, true));
+ }
+ }
+ n = invisibleParameterAnnotations == null
+ ? 0
+ : invisibleParameterAnnotations.length;
+ for (i = 0; i < n; ++i) {
+ List l = invisibleParameterAnnotations[i];
+ if (l == null) {
+ continue;
+ }
+ for (j = 0; j < l.size(); ++j) {
+ AnnotationNode an = (AnnotationNode) l.get(j);
+ an.accept(mv.visitParameterAnnotation(i, an.desc, false));
+ }
+ }
+ n = attrs == null ? 0 : attrs.size();
+ for (i = 0; i < n; ++i) {
+ mv.visitAttribute((Attribute) attrs.get(i));
+ }
+ // visits the method's code
+ if (instructions.size() > 0) {
+ mv.visitCode();
+ // visits try catch blocks
+ for (i = 0; i < tryCatchBlocks.size(); ++i) {
+ ((TryCatchBlockNode) tryCatchBlocks.get(i)).accept(mv);
+ }
+ // visits instructions
+ instructions.accept(mv);
+ // visits local variables
+ n = localVariables == null ? 0 : localVariables.size();
+ for (i = 0; i < n; ++i) {
+ ((LocalVariableNode) localVariables.get(i)).accept(mv);
+ }
+ // visits maxs
+ mv.visitMaxs(maxStack, maxLocals);
+ }
+ mv.visitEnd();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/MultiANewArrayInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/MultiANewArrayInsnNode.java
new file mode 100644
index 0000000..efc9dc1
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/MultiANewArrayInsnNode.java
@@ -0,0 +1,78 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+
+/**
+ * A node that represents a MULTIANEWARRAY instruction.
+ *
+ * @author Eric Bruneton
+ */
+public class MultiANewArrayInsnNode extends AbstractInsnNode {
+
+ /**
+ * An array type descriptor (see {@link org.mockito.asm.Type}).
+ */
+ public String desc;
+
+ /**
+ * Number of dimensions of the array to allocate.
+ */
+ public int dims;
+
+ /**
+ * Constructs a new {@link MultiANewArrayInsnNode}.
+ *
+ * @param desc an array type descriptor (see {@link org.mockito.asm.Type}).
+ * @param dims number of dimensions of the array to allocate.
+ */
+ public MultiANewArrayInsnNode(final String desc, final int dims) {
+ super(Opcodes.MULTIANEWARRAY);
+ this.desc = desc;
+ this.dims = dims;
+ }
+
+ public int getType() {
+ return MULTIANEWARRAY_INSN;
+ }
+
+ public void accept(final MethodVisitor mv) {
+ mv.visitMultiANewArrayInsn(desc, dims);
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new MultiANewArrayInsnNode(desc, dims);
+ }
+
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/TableSwitchInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/TableSwitchInsnNode.java
new file mode 100644
index 0000000..fe038b4
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/TableSwitchInsnNode.java
@@ -0,0 +1,112 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import org.mockito.asm.Label;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A node that represents a TABLESWITCH instruction.
+ *
+ * @author Eric Bruneton
+ */
+public class TableSwitchInsnNode extends AbstractInsnNode {
+
+ /**
+ * The minimum key value.
+ */
+ public int min;
+
+ /**
+ * The maximum key value.
+ */
+ public int max;
+
+ /**
+ * Beginning of the default handler block.
+ */
+ public LabelNode dflt;
+
+ /**
+ * Beginnings of the handler blocks. This list is a list of
+ * {@link LabelNode} objects.
+ */
+ public List labels;
+
+ /**
+ * Constructs a new {@link TableSwitchInsnNode}.
+ *
+ * @param min the minimum key value.
+ * @param max the maximum key value.
+ * @param dflt beginning of the default handler block.
+ * @param labels beginnings of the handler blocks. <tt>labels[i]</tt> is
+ * the beginning of the handler block for the <tt>min + i</tt> key.
+ */
+ public TableSwitchInsnNode(
+ final int min,
+ final int max,
+ final LabelNode dflt,
+ final LabelNode[] labels)
+ {
+ super(Opcodes.TABLESWITCH);
+ this.min = min;
+ this.max = max;
+ this.dflt = dflt;
+ this.labels = new ArrayList();
+ if (labels != null) {
+ this.labels.addAll(Arrays.asList(labels));
+ }
+ }
+
+ public int getType() {
+ return TABLESWITCH_INSN;
+ }
+
+ public void accept(final MethodVisitor mv) {
+ Label[] labels = new Label[this.labels.size()];
+ for (int i = 0; i < labels.length; ++i) {
+ labels[i] = ((LabelNode) this.labels.get(i)).getLabel();
+ }
+ mv.visitTableSwitchInsn(min, max, dflt.getLabel(), labels);
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new TableSwitchInsnNode(min,
+ max,
+ clone(dflt, labels),
+ clone(this.labels, labels));
+ }
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/TryCatchBlockNode.java b/cglib-and-asm/src/org/mockito/asm/tree/TryCatchBlockNode.java
new file mode 100644
index 0000000..8d23f14
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/TryCatchBlockNode.java
@@ -0,0 +1,94 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A node that represents a try catch block.
+ *
+ * @author Eric Bruneton
+ */
+public class TryCatchBlockNode {
+
+ /**
+ * Beginning of the exception handler's scope (inclusive).
+ */
+ public LabelNode start;
+
+ /**
+ * End of the exception handler's scope (exclusive).
+ */
+ public LabelNode end;
+
+ /**
+ * Beginning of the exception handler's code.
+ */
+ public LabelNode handler;
+
+ /**
+ * Internal name of the type of exceptions handled by the handler. May be
+ * <tt>null</tt> to catch any exceptions (for "finally" blocks).
+ */
+ public String type;
+
+ /**
+ * Constructs a new {@link TryCatchBlockNode}.
+ *
+ * @param start beginning of the exception handler's scope (inclusive).
+ * @param end end of the exception handler's scope (exclusive).
+ * @param handler beginning of the exception handler's code.
+ * @param type internal name of the type of exceptions handled by the
+ * handler, or <tt>null</tt> to catch any exceptions (for "finally"
+ * blocks).
+ */
+ public TryCatchBlockNode(
+ final LabelNode start,
+ final LabelNode end,
+ final LabelNode handler,
+ final String type)
+ {
+ this.start = start;
+ this.end = end;
+ this.handler = handler;
+ this.type = type;
+ }
+
+ /**
+ * Makes the given visitor visit this try catch block.
+ *
+ * @param mv a method visitor.
+ */
+ public void accept(final MethodVisitor mv) {
+ mv.visitTryCatchBlock(start.getLabel(), end.getLabel(), handler == null
+ ? null
+ : handler.getLabel(), type);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/TypeInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/TypeInsnNode.java
new file mode 100644
index 0000000..eeeab03
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/TypeInsnNode.java
@@ -0,0 +1,84 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A node that represents a type instruction. A type instruction is an
+ * instruction that takes a type descriptor as parameter.
+ *
+ * @author Eric Bruneton
+ */
+public class TypeInsnNode extends AbstractInsnNode {
+
+ /**
+ * The operand of this instruction. This operand is an internal name (see
+ * {@link org.mockito.asm.Type}).
+ */
+ public String desc;
+
+ /**
+ * Constructs a new {@link TypeInsnNode}.
+ *
+ * @param opcode the opcode of the type instruction to be constructed. This
+ * opcode must be NEW, ANEWARRAY, CHECKCAST or INSTANCEOF.
+ * @param desc the operand of the instruction to be constructed. This
+ * operand is an internal name (see {@link org.mockito.asm.Type}).
+ */
+ public TypeInsnNode(final int opcode, final String desc) {
+ super(opcode);
+ this.desc = desc;
+ }
+
+ /**
+ * Sets the opcode of this instruction.
+ *
+ * @param opcode the new instruction opcode. This opcode must be NEW,
+ * ANEWARRAY, CHECKCAST or INSTANCEOF.
+ */
+ public void setOpcode(final int opcode) {
+ this.opcode = opcode;
+ }
+
+ public int getType() {
+ return TYPE_INSN;
+ }
+
+ public void accept(final MethodVisitor mv) {
+ mv.visitTypeInsn(opcode, desc);
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new TypeInsnNode(opcode, desc);
+ }
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/VarInsnNode.java b/cglib-and-asm/src/org/mockito/asm/tree/VarInsnNode.java
new file mode 100644
index 0000000..b8d65d7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/VarInsnNode.java
@@ -0,0 +1,87 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree;
+
+import java.util.Map;
+
+import org.mockito.asm.MethodVisitor;
+
+/**
+ * A node that represents a local variable instruction. A local variable
+ * instruction is an instruction that loads or stores the value of a local
+ * variable.
+ *
+ * @author Eric Bruneton
+ */
+public class VarInsnNode extends AbstractInsnNode {
+
+ /**
+ * The operand of this instruction. This operand is the index of a local
+ * variable.
+ */
+ public int var;
+
+ /**
+ * Constructs a new {@link VarInsnNode}.
+ *
+ * @param opcode the opcode of the local variable instruction to be
+ * constructed. This opcode must be ILOAD, LLOAD, FLOAD, DLOAD,
+ * ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET.
+ * @param var the operand of the instruction to be constructed. This operand
+ * is the index of a local variable.
+ */
+ public VarInsnNode(final int opcode, final int var) {
+ super(opcode);
+ this.var = var;
+ }
+
+ /**
+ * Sets the opcode of this instruction.
+ *
+ * @param opcode the new instruction opcode. This opcode must be ILOAD,
+ * LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE
+ * or RET.
+ */
+ public void setOpcode(final int opcode) {
+ this.opcode = opcode;
+ }
+
+ public int getType() {
+ return VAR_INSN;
+ }
+
+ public void accept(final MethodVisitor mv) {
+ mv.visitVarInsn(opcode, var);
+ }
+
+ public AbstractInsnNode clone(final Map labels) {
+ return new VarInsnNode(opcode, var);
+ }
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/Analyzer.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/Analyzer.java
new file mode 100644
index 0000000..3888146
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/Analyzer.java
@@ -0,0 +1,507 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.mockito.asm.Opcodes;
+import org.mockito.asm.Type;
+import org.mockito.asm.tree.AbstractInsnNode;
+import org.mockito.asm.tree.IincInsnNode;
+import org.mockito.asm.tree.InsnList;
+import org.mockito.asm.tree.JumpInsnNode;
+import org.mockito.asm.tree.LabelNode;
+import org.mockito.asm.tree.LookupSwitchInsnNode;
+import org.mockito.asm.tree.MethodNode;
+import org.mockito.asm.tree.TableSwitchInsnNode;
+import org.mockito.asm.tree.TryCatchBlockNode;
+import org.mockito.asm.tree.VarInsnNode;
+
+/**
+ * A semantic bytecode analyzer. <i>This class does not fully check that JSR and
+ * RET instructions are valid.</i>
+ *
+ * @author Eric Bruneton
+ */
+public class Analyzer implements Opcodes {
+
+ private final Interpreter interpreter;
+
+ private int n;
+
+ private InsnList insns;
+
+ private List[] handlers;
+
+ private Frame[] frames;
+
+ private Subroutine[] subroutines;
+
+ private boolean[] queued;
+
+ private int[] queue;
+
+ private int top;
+
+ /**
+ * Constructs a new {@link Analyzer}.
+ *
+ * @param interpreter the interpreter to be used to symbolically interpret
+ * the bytecode instructions.
+ */
+ public Analyzer(final Interpreter interpreter) {
+ this.interpreter = interpreter;
+ }
+
+ /**
+ * Analyzes the given method.
+ *
+ * @param owner the internal name of the class to which the method belongs.
+ * @param m the method to be analyzed.
+ * @return the symbolic state of the execution stack frame at each bytecode
+ * instruction of the method. The size of the returned array is
+ * equal to the number of instructions (and labels) of the method. A
+ * given frame is <tt>null</tt> if and only if the corresponding
+ * instruction cannot be reached (dead code).
+ * @throws AnalyzerException if a problem occurs during the analysis.
+ */
+ public Frame[] analyze(final String owner, final MethodNode m)
+ throws AnalyzerException
+ {
+ if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {
+ frames = new Frame[0];
+ return frames;
+ }
+ n = m.instructions.size();
+ insns = m.instructions;
+ handlers = new List[n];
+ frames = new Frame[n];
+ subroutines = new Subroutine[n];
+ queued = new boolean[n];
+ queue = new int[n];
+ top = 0;
+
+ // computes exception handlers for each instruction
+ for (int i = 0; i < m.tryCatchBlocks.size(); ++i) {
+ TryCatchBlockNode tcb = (TryCatchBlockNode) m.tryCatchBlocks.get(i);
+ int begin = insns.indexOf(tcb.start);
+ int end = insns.indexOf(tcb.end);
+ for (int j = begin; j < end; ++j) {
+ List insnHandlers = handlers[j];
+ if (insnHandlers == null) {
+ insnHandlers = new ArrayList();
+ handlers[j] = insnHandlers;
+ }
+ insnHandlers.add(tcb);
+ }
+ }
+
+ // computes the subroutine for each instruction:
+ Subroutine main = new Subroutine(null, m.maxLocals, null);
+ List subroutineCalls = new ArrayList();
+ Map subroutineHeads = new HashMap();
+ findSubroutine(0, main, subroutineCalls);
+ while (!subroutineCalls.isEmpty()) {
+ JumpInsnNode jsr = (JumpInsnNode) subroutineCalls.remove(0);
+ Subroutine sub = (Subroutine) subroutineHeads.get(jsr.label);
+ if (sub == null) {
+ sub = new Subroutine(jsr.label, m.maxLocals, jsr);
+ subroutineHeads.put(jsr.label, sub);
+ findSubroutine(insns.indexOf(jsr.label), sub, subroutineCalls);
+ } else {
+ sub.callers.add(jsr);
+ }
+ }
+ for (int i = 0; i < n; ++i) {
+ if (subroutines[i] != null && subroutines[i].start == null) {
+ subroutines[i] = null;
+ }
+ }
+
+ // initializes the data structures for the control flow analysis
+ Frame current = newFrame(m.maxLocals, m.maxStack);
+ Frame handler = newFrame(m.maxLocals, m.maxStack);
+ Type[] args = Type.getArgumentTypes(m.desc);
+ int local = 0;
+ if ((m.access & ACC_STATIC) == 0) {
+ Type ctype = Type.getObjectType(owner);
+ current.setLocal(local++, interpreter.newValue(ctype));
+ }
+ for (int i = 0; i < args.length; ++i) {
+ current.setLocal(local++, interpreter.newValue(args[i]));
+ if (args[i].getSize() == 2) {
+ current.setLocal(local++, interpreter.newValue(null));
+ }
+ }
+ while (local < m.maxLocals) {
+ current.setLocal(local++, interpreter.newValue(null));
+ }
+ merge(0, current, null);
+
+ // control flow analysis
+ while (top > 0) {
+ int insn = queue[--top];
+ Frame f = frames[insn];
+ Subroutine subroutine = subroutines[insn];
+ queued[insn] = false;
+
+ try {
+ AbstractInsnNode insnNode = m.instructions.get(insn);
+ int insnOpcode = insnNode.getOpcode();
+ int insnType = insnNode.getType();
+
+ if (insnType == AbstractInsnNode.LABEL
+ || insnType == AbstractInsnNode.LINE
+ || insnType == AbstractInsnNode.FRAME)
+ {
+ merge(insn + 1, f, subroutine);
+ newControlFlowEdge(insn, insn + 1);
+ } else {
+ current.init(f).execute(insnNode, interpreter);
+ subroutine = subroutine == null ? null : subroutine.copy();
+
+ if (insnNode instanceof JumpInsnNode) {
+ JumpInsnNode j = (JumpInsnNode) insnNode;
+ if (insnOpcode != GOTO && insnOpcode != JSR) {
+ merge(insn + 1, current, subroutine);
+ newControlFlowEdge(insn, insn + 1);
+ }
+ int jump = insns.indexOf(j.label);
+ if (insnOpcode == JSR) {
+ merge(jump, current, new Subroutine(j.label,
+ m.maxLocals,
+ j));
+ } else {
+ merge(jump, current, subroutine);
+ }
+ newControlFlowEdge(insn, jump);
+ } else if (insnNode instanceof LookupSwitchInsnNode) {
+ LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode;
+ int jump = insns.indexOf(lsi.dflt);
+ merge(jump, current, subroutine);
+ newControlFlowEdge(insn, jump);
+ for (int j = 0; j < lsi.labels.size(); ++j) {
+ LabelNode label = (LabelNode) lsi.labels.get(j);
+ jump = insns.indexOf(label);
+ merge(jump, current, subroutine);
+ newControlFlowEdge(insn, jump);
+ }
+ } else if (insnNode instanceof TableSwitchInsnNode) {
+ TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode;
+ int jump = insns.indexOf(tsi.dflt);
+ merge(jump, current, subroutine);
+ newControlFlowEdge(insn, jump);
+ for (int j = 0; j < tsi.labels.size(); ++j) {
+ LabelNode label = (LabelNode) tsi.labels.get(j);
+ jump = insns.indexOf(label);
+ merge(jump, current, subroutine);
+ newControlFlowEdge(insn, jump);
+ }
+ } else if (insnOpcode == RET) {
+ if (subroutine == null) {
+ throw new AnalyzerException("RET instruction outside of a sub routine");
+ }
+ for (int i = 0; i < subroutine.callers.size(); ++i) {
+ Object caller = subroutine.callers.get(i);
+ int call = insns.indexOf((AbstractInsnNode) caller);
+ if (frames[call] != null) {
+ merge(call + 1,
+ frames[call],
+ current,
+ subroutines[call],
+ subroutine.access);
+ newControlFlowEdge(insn, call + 1);
+ }
+ }
+ } else if (insnOpcode != ATHROW
+ && (insnOpcode < IRETURN || insnOpcode > RETURN))
+ {
+ if (subroutine != null) {
+ if (insnNode instanceof VarInsnNode) {
+ int var = ((VarInsnNode) insnNode).var;
+ subroutine.access[var] = true;
+ if (insnOpcode == LLOAD || insnOpcode == DLOAD
+ || insnOpcode == LSTORE
+ || insnOpcode == DSTORE)
+ {
+ subroutine.access[var + 1] = true;
+ }
+ } else if (insnNode instanceof IincInsnNode) {
+ int var = ((IincInsnNode) insnNode).var;
+ subroutine.access[var] = true;
+ }
+ }
+ merge(insn + 1, current, subroutine);
+ newControlFlowEdge(insn, insn + 1);
+ }
+ }
+
+ List insnHandlers = handlers[insn];
+ if (insnHandlers != null) {
+ for (int i = 0; i < insnHandlers.size(); ++i) {
+ TryCatchBlockNode tcb = (TryCatchBlockNode) insnHandlers.get(i);
+ Type type;
+ if (tcb.type == null) {
+ type = Type.getObjectType("java/lang/Throwable");
+ } else {
+ type = Type.getObjectType(tcb.type);
+ }
+ int jump = insns.indexOf(tcb.handler);
+ if (newControlFlowExceptionEdge(insn, jump)) {
+ handler.init(f);
+ handler.clearStack();
+ handler.push(interpreter.newValue(type));
+ merge(jump, handler, subroutine);
+ }
+ }
+ }
+ } catch (AnalyzerException e) {
+ throw new AnalyzerException("Error at instruction " + insn
+ + ": " + e.getMessage(), e);
+ } catch (Exception e) {
+ throw new AnalyzerException("Error at instruction " + insn
+ + ": " + e.getMessage(), e);
+ }
+ }
+
+ return frames;
+ }
+
+ private void findSubroutine(int insn, final Subroutine sub, final List calls)
+ throws AnalyzerException
+ {
+ while (true) {
+ if (insn < 0 || insn >= n) {
+ throw new AnalyzerException("Execution can fall off end of the code");
+ }
+ if (subroutines[insn] != null) {
+ return;
+ }
+ subroutines[insn] = sub.copy();
+ AbstractInsnNode node = insns.get(insn);
+
+ // calls findSubroutine recursively on normal successors
+ if (node instanceof JumpInsnNode) {
+ if (node.getOpcode() == JSR) {
+ // do not follow a JSR, it leads to another subroutine!
+ calls.add(node);
+ } else {
+ JumpInsnNode jnode = (JumpInsnNode) node;
+ findSubroutine(insns.indexOf(jnode.label), sub, calls);
+ }
+ } else if (node instanceof TableSwitchInsnNode) {
+ TableSwitchInsnNode tsnode = (TableSwitchInsnNode) node;
+ findSubroutine(insns.indexOf(tsnode.dflt), sub, calls);
+ for (int i = tsnode.labels.size() - 1; i >= 0; --i) {
+ LabelNode l = (LabelNode) tsnode.labels.get(i);
+ findSubroutine(insns.indexOf(l), sub, calls);
+ }
+ } else if (node instanceof LookupSwitchInsnNode) {
+ LookupSwitchInsnNode lsnode = (LookupSwitchInsnNode) node;
+ findSubroutine(insns.indexOf(lsnode.dflt), sub, calls);
+ for (int i = lsnode.labels.size() - 1; i >= 0; --i) {
+ LabelNode l = (LabelNode) lsnode.labels.get(i);
+ findSubroutine(insns.indexOf(l), sub, calls);
+ }
+ }
+
+ // calls findSubroutine recursively on exception handler successors
+ List insnHandlers = handlers[insn];
+ if (insnHandlers != null) {
+ for (int i = 0; i < insnHandlers.size(); ++i) {
+ TryCatchBlockNode tcb = (TryCatchBlockNode) insnHandlers.get(i);
+ findSubroutine(insns.indexOf(tcb.handler), sub, calls);
+ }
+ }
+
+ // if insn does not falls through to the next instruction, return.
+ switch (node.getOpcode()) {
+ case GOTO:
+ case RET:
+ case TABLESWITCH:
+ case LOOKUPSWITCH:
+ case IRETURN:
+ case LRETURN:
+ case FRETURN:
+ case DRETURN:
+ case ARETURN:
+ case RETURN:
+ case ATHROW:
+ return;
+ }
+ insn++;
+ }
+ }
+
+ /**
+ * Returns the symbolic stack frame for each instruction of the last
+ * recently analyzed method.
+ *
+ * @return the symbolic state of the execution stack frame at each bytecode
+ * instruction of the method. The size of the returned array is
+ * equal to the number of instructions (and labels) of the method. A
+ * given frame is <tt>null</tt> if the corresponding instruction
+ * cannot be reached, or if an error occured during the analysis of
+ * the method.
+ */
+ public Frame[] getFrames() {
+ return frames;
+ }
+
+ /**
+ * Returns the exception handlers for the given instruction.
+ *
+ * @param insn the index of an instruction of the last recently analyzed
+ * method.
+ * @return a list of {@link TryCatchBlockNode} objects.
+ */
+ public List getHandlers(final int insn) {
+ return handlers[insn];
+ }
+
+ /**
+ * Constructs a new frame with the given size.
+ *
+ * @param nLocals the maximum number of local variables of the frame.
+ * @param nStack the maximum stack size of the frame.
+ * @return the created frame.
+ */
+ protected Frame newFrame(final int nLocals, final int nStack) {
+ return new Frame(nLocals, nStack);
+ }
+
+ /**
+ * Constructs a new frame that is identical to the given frame.
+ *
+ * @param src a frame.
+ * @return the created frame.
+ */
+ protected Frame newFrame(final Frame src) {
+ return new Frame(src);
+ }
+
+ /**
+ * Creates a control flow graph edge. The default implementation of this
+ * method does nothing. It can be overriden in order to construct the
+ * control flow graph of a method (this method is called by the
+ * {@link #analyze analyze} method during its visit of the method's code).
+ *
+ * @param insn an instruction index.
+ * @param successor index of a successor instruction.
+ */
+ protected void newControlFlowEdge(final int insn, final int successor) {
+ }
+
+ /**
+ * Creates a control flow graph edge corresponding to an exception handler.
+ * The default implementation of this method does nothing. It can be
+ * overriden in order to construct the control flow graph of a method (this
+ * method is called by the {@link #analyze analyze} method during its visit
+ * of the method's code).
+ *
+ * @param insn an instruction index.
+ * @param successor index of a successor instruction.
+ * @return true if this edge must be considered in the data flow analysis
+ * performed by this analyzer, or false otherwise. The default
+ * implementation of this method always returns true.
+ */
+ protected boolean newControlFlowExceptionEdge(
+ final int insn,
+ final int successor)
+ {
+ return true;
+ }
+
+ // -------------------------------------------------------------------------
+
+ private void merge(
+ final int insn,
+ final Frame frame,
+ final Subroutine subroutine) throws AnalyzerException
+ {
+ Frame oldFrame = frames[insn];
+ Subroutine oldSubroutine = subroutines[insn];
+ boolean changes;
+
+ if (oldFrame == null) {
+ frames[insn] = newFrame(frame);
+ changes = true;
+ } else {
+ changes = oldFrame.merge(frame, interpreter);
+ }
+
+ if (oldSubroutine == null) {
+ if (subroutine != null) {
+ subroutines[insn] = subroutine.copy();
+ changes = true;
+ }
+ } else {
+ if (subroutine != null) {
+ changes |= oldSubroutine.merge(subroutine);
+ }
+ }
+ if (changes && !queued[insn]) {
+ queued[insn] = true;
+ queue[top++] = insn;
+ }
+ }
+
+ private void merge(
+ final int insn,
+ final Frame beforeJSR,
+ final Frame afterRET,
+ final Subroutine subroutineBeforeJSR,
+ final boolean[] access) throws AnalyzerException
+ {
+ Frame oldFrame = frames[insn];
+ Subroutine oldSubroutine = subroutines[insn];
+ boolean changes;
+
+ afterRET.merge(beforeJSR, access);
+
+ if (oldFrame == null) {
+ frames[insn] = newFrame(afterRET);
+ changes = true;
+ } else {
+ changes = oldFrame.merge(afterRET, access);
+ }
+
+ if (oldSubroutine != null && subroutineBeforeJSR != null) {
+ changes |= oldSubroutine.merge(subroutineBeforeJSR);
+ }
+ if (changes && !queued[insn]) {
+ queued[insn] = true;
+ queue[top++] = insn;
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/AnalyzerException.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/AnalyzerException.java
new file mode 100644
index 0000000..6cf96e8
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/AnalyzerException.java
@@ -0,0 +1,56 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+/**
+ * Thrown if a problem occurs during the analysis of a method.
+ *
+ * @author Bing Ran
+ * @author Eric Bruneton
+ */
+public class AnalyzerException extends Exception {
+
+ public AnalyzerException(final String msg) {
+ super(msg);
+ }
+
+ public AnalyzerException(final String msg, final Throwable exception) {
+ super(msg, exception);
+ }
+
+ public AnalyzerException(
+ final String msg,
+ final Object expected,
+ final Value encountered)
+ {
+ super((msg == null ? "Expected " : msg + ": expected ") + expected
+ + ", but found " + encountered);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicInterpreter.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicInterpreter.java
new file mode 100644
index 0000000..4b4de8c
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicInterpreter.java
@@ -0,0 +1,321 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+import java.util.List;
+
+import org.mockito.asm.Opcodes;
+import org.mockito.asm.Type;
+import org.mockito.asm.tree.AbstractInsnNode;
+import org.mockito.asm.tree.FieldInsnNode;
+import org.mockito.asm.tree.IntInsnNode;
+import org.mockito.asm.tree.LdcInsnNode;
+import org.mockito.asm.tree.MethodInsnNode;
+import org.mockito.asm.tree.MultiANewArrayInsnNode;
+import org.mockito.asm.tree.TypeInsnNode;
+
+/**
+ * An {@link Interpreter} for {@link BasicValue} values.
+ *
+ * @author Eric Bruneton
+ * @author Bing Ran
+ */
+public class BasicInterpreter implements Opcodes, Interpreter {
+
+ public Value newValue(final Type type) {
+ if (type == null) {
+ return BasicValue.UNINITIALIZED_VALUE;
+ }
+ switch (type.getSort()) {
+ case Type.VOID:
+ return null;
+ case Type.BOOLEAN:
+ case Type.CHAR:
+ case Type.BYTE:
+ case Type.SHORT:
+ case Type.INT:
+ return BasicValue.INT_VALUE;
+ case Type.FLOAT:
+ return BasicValue.FLOAT_VALUE;
+ case Type.LONG:
+ return BasicValue.LONG_VALUE;
+ case Type.DOUBLE:
+ return BasicValue.DOUBLE_VALUE;
+ case Type.ARRAY:
+ case Type.OBJECT:
+ return BasicValue.REFERENCE_VALUE;
+ default:
+ throw new Error("Internal error");
+ }
+ }
+
+ public Value newOperation(final AbstractInsnNode insn) {
+ switch (insn.getOpcode()) {
+ case ACONST_NULL:
+ return newValue(Type.getObjectType("null"));
+ case ICONST_M1:
+ case ICONST_0:
+ case ICONST_1:
+ case ICONST_2:
+ case ICONST_3:
+ case ICONST_4:
+ case ICONST_5:
+ return BasicValue.INT_VALUE;
+ case LCONST_0:
+ case LCONST_1:
+ return BasicValue.LONG_VALUE;
+ case FCONST_0:
+ case FCONST_1:
+ case FCONST_2:
+ return BasicValue.FLOAT_VALUE;
+ case DCONST_0:
+ case DCONST_1:
+ return BasicValue.DOUBLE_VALUE;
+ case BIPUSH:
+ case SIPUSH:
+ return BasicValue.INT_VALUE;
+ case LDC:
+ Object cst = ((LdcInsnNode) insn).cst;
+ if (cst instanceof Integer) {
+ return BasicValue.INT_VALUE;
+ } else if (cst instanceof Float) {
+ return BasicValue.FLOAT_VALUE;
+ } else if (cst instanceof Long) {
+ return BasicValue.LONG_VALUE;
+ } else if (cst instanceof Double) {
+ return BasicValue.DOUBLE_VALUE;
+ } else if (cst instanceof Type) {
+ return newValue(Type.getObjectType("java/lang/Class"));
+ } else {
+ return newValue(Type.getType(cst.getClass()));
+ }
+ case JSR:
+ return BasicValue.RETURNADDRESS_VALUE;
+ case GETSTATIC:
+ return newValue(Type.getType(((FieldInsnNode) insn).desc));
+ case NEW:
+ return newValue(Type.getObjectType(((TypeInsnNode) insn).desc));
+ default:
+ throw new Error("Internal error.");
+ }
+ }
+
+ public Value copyOperation(final AbstractInsnNode insn, final Value value)
+ throws AnalyzerException
+ {
+ return value;
+ }
+
+ public Value unaryOperation(final AbstractInsnNode insn, final Value value)
+ throws AnalyzerException
+ {
+ switch (insn.getOpcode()) {
+ case INEG:
+ case IINC:
+ case L2I:
+ case F2I:
+ case D2I:
+ case I2B:
+ case I2C:
+ case I2S:
+ return BasicValue.INT_VALUE;
+ case FNEG:
+ case I2F:
+ case L2F:
+ case D2F:
+ return BasicValue.FLOAT_VALUE;
+ case LNEG:
+ case I2L:
+ case F2L:
+ case D2L:
+ return BasicValue.LONG_VALUE;
+ case DNEG:
+ case I2D:
+ case L2D:
+ case F2D:
+ return BasicValue.DOUBLE_VALUE;
+ case IFEQ:
+ case IFNE:
+ case IFLT:
+ case IFGE:
+ case IFGT:
+ case IFLE:
+ case TABLESWITCH:
+ case LOOKUPSWITCH:
+ case IRETURN:
+ case LRETURN:
+ case FRETURN:
+ case DRETURN:
+ case ARETURN:
+ case PUTSTATIC:
+ return null;
+ case GETFIELD:
+ return newValue(Type.getType(((FieldInsnNode) insn).desc));
+ case NEWARRAY:
+ switch (((IntInsnNode) insn).operand) {
+ case T_BOOLEAN:
+ return newValue(Type.getType("[Z"));
+ case T_CHAR:
+ return newValue(Type.getType("[C"));
+ case T_BYTE:
+ return newValue(Type.getType("[B"));
+ case T_SHORT:
+ return newValue(Type.getType("[S"));
+ case T_INT:
+ return newValue(Type.getType("[I"));
+ case T_FLOAT:
+ return newValue(Type.getType("[F"));
+ case T_DOUBLE:
+ return newValue(Type.getType("[D"));
+ case T_LONG:
+ return newValue(Type.getType("[J"));
+ default:
+ throw new AnalyzerException("Invalid array type");
+ }
+ case ANEWARRAY:
+ String desc = ((TypeInsnNode) insn).desc;
+ return newValue(Type.getType("[" + Type.getObjectType(desc)));
+ case ARRAYLENGTH:
+ return BasicValue.INT_VALUE;
+ case ATHROW:
+ return null;
+ case CHECKCAST:
+ desc = ((TypeInsnNode) insn).desc;
+ return newValue(Type.getObjectType(desc));
+ case INSTANCEOF:
+ return BasicValue.INT_VALUE;
+ case MONITORENTER:
+ case MONITOREXIT:
+ case IFNULL:
+ case IFNONNULL:
+ return null;
+ default:
+ throw new Error("Internal error.");
+ }
+ }
+
+ public Value binaryOperation(
+ final AbstractInsnNode insn,
+ final Value value1,
+ final Value value2) throws AnalyzerException
+ {
+ switch (insn.getOpcode()) {
+ case IALOAD:
+ case BALOAD:
+ case CALOAD:
+ case SALOAD:
+ case IADD:
+ case ISUB:
+ case IMUL:
+ case IDIV:
+ case IREM:
+ case ISHL:
+ case ISHR:
+ case IUSHR:
+ case IAND:
+ case IOR:
+ case IXOR:
+ return BasicValue.INT_VALUE;
+ case FALOAD:
+ case FADD:
+ case FSUB:
+ case FMUL:
+ case FDIV:
+ case FREM:
+ return BasicValue.FLOAT_VALUE;
+ case LALOAD:
+ case LADD:
+ case LSUB:
+ case LMUL:
+ case LDIV:
+ case LREM:
+ case LSHL:
+ case LSHR:
+ case LUSHR:
+ case LAND:
+ case LOR:
+ case LXOR:
+ return BasicValue.LONG_VALUE;
+ case DALOAD:
+ case DADD:
+ case DSUB:
+ case DMUL:
+ case DDIV:
+ case DREM:
+ return BasicValue.DOUBLE_VALUE;
+ case AALOAD:
+ return BasicValue.REFERENCE_VALUE;
+ case LCMP:
+ case FCMPL:
+ case FCMPG:
+ case DCMPL:
+ case DCMPG:
+ return BasicValue.INT_VALUE;
+ case IF_ICMPEQ:
+ case IF_ICMPNE:
+ case IF_ICMPLT:
+ case IF_ICMPGE:
+ case IF_ICMPGT:
+ case IF_ICMPLE:
+ case IF_ACMPEQ:
+ case IF_ACMPNE:
+ case PUTFIELD:
+ return null;
+ default:
+ throw new Error("Internal error.");
+ }
+ }
+
+ public Value ternaryOperation(
+ final AbstractInsnNode insn,
+ final Value value1,
+ final Value value2,
+ final Value value3) throws AnalyzerException
+ {
+ return null;
+ }
+
+ public Value naryOperation(final AbstractInsnNode insn, final List values)
+ throws AnalyzerException
+ {
+ if (insn.getOpcode() == MULTIANEWARRAY) {
+ return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc));
+ } else {
+ return newValue(Type.getReturnType(((MethodInsnNode) insn).desc));
+ }
+ }
+
+ public Value merge(final Value v, final Value w) {
+ if (!v.equals(w)) {
+ return BasicValue.UNINITIALIZED_VALUE;
+ }
+ return v;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicValue.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicValue.java
new file mode 100644
index 0000000..d772dbe
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicValue.java
@@ -0,0 +1,105 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+import org.mockito.asm.Type;
+
+/**
+ * A {@link Value} that is represented by its type in a seven types type system.
+ * This type system distinguishes the UNINITIALZED, INT, FLOAT, LONG, DOUBLE,
+ * REFERENCE and RETURNADDRESS types.
+ *
+ * @author Eric Bruneton
+ */
+public class BasicValue implements Value {
+
+ public static final Value UNINITIALIZED_VALUE = new BasicValue(null);
+
+ public static final Value INT_VALUE = new BasicValue(Type.INT_TYPE);
+
+ public static final Value FLOAT_VALUE = new BasicValue(Type.FLOAT_TYPE);
+
+ public static final Value LONG_VALUE = new BasicValue(Type.LONG_TYPE);
+
+ public static final Value DOUBLE_VALUE = new BasicValue(Type.DOUBLE_TYPE);
+
+ public static final Value REFERENCE_VALUE = new BasicValue(Type.getObjectType("java/lang/Object"));
+
+ public static final Value RETURNADDRESS_VALUE = new BasicValue(null);
+
+ private final Type type;
+
+ public BasicValue(final Type type) {
+ this.type = type;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public int getSize() {
+ return type == Type.LONG_TYPE || type == Type.DOUBLE_TYPE ? 2 : 1;
+ }
+
+ public boolean isReference() {
+ return type != null
+ && (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY);
+ }
+
+ public boolean equals(final Object value) {
+ if (value == this) {
+ return true;
+ } else if (value instanceof BasicValue) {
+ if (type == null) {
+ return ((BasicValue) value).type == null;
+ } else {
+ return type.equals(((BasicValue) value).type);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ public int hashCode() {
+ return type == null ? 0 : type.hashCode();
+ }
+
+ public String toString() {
+ if (this == UNINITIALIZED_VALUE) {
+ return ".";
+ } else if (this == RETURNADDRESS_VALUE) {
+ return "A";
+ } else if (this == REFERENCE_VALUE) {
+ return "R";
+ } else {
+ return type.getDescriptor();
+ }
+ }
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicVerifier.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicVerifier.java
new file mode 100644
index 0000000..beba62c
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/BasicVerifier.java
@@ -0,0 +1,423 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+import java.util.List;
+
+import org.mockito.asm.Type;
+import org.mockito.asm.tree.AbstractInsnNode;
+import org.mockito.asm.tree.FieldInsnNode;
+import org.mockito.asm.tree.MethodInsnNode;
+
+/**
+ * An extended {@link BasicInterpreter} that checks that bytecode instructions
+ * are correctly used.
+ *
+ * @author Eric Bruneton
+ * @author Bing Ran
+ */
+public class BasicVerifier extends BasicInterpreter {
+
+ public Value copyOperation(final AbstractInsnNode insn, final Value value)
+ throws AnalyzerException
+ {
+ Value expected;
+ switch (insn.getOpcode()) {
+ case ILOAD:
+ case ISTORE:
+ expected = BasicValue.INT_VALUE;
+ break;
+ case FLOAD:
+ case FSTORE:
+ expected = BasicValue.FLOAT_VALUE;
+ break;
+ case LLOAD:
+ case LSTORE:
+ expected = BasicValue.LONG_VALUE;
+ break;
+ case DLOAD:
+ case DSTORE:
+ expected = BasicValue.DOUBLE_VALUE;
+ break;
+ case ALOAD:
+ if (!((BasicValue) value).isReference()) {
+ throw new AnalyzerException(null,
+ "an object reference",
+ value);
+ }
+ return value;
+ case ASTORE:
+ if (!((BasicValue) value).isReference()
+ && value != BasicValue.RETURNADDRESS_VALUE)
+ {
+ throw new AnalyzerException(null,
+ "an object reference or a return address",
+ value);
+ }
+ return value;
+ default:
+ return value;
+ }
+ // type is necessarily a primitive type here,
+ // so value must be == to expected value
+ if (value != expected) {
+ throw new AnalyzerException(null, expected, value);
+ }
+ return value;
+ }
+
+ public Value unaryOperation(final AbstractInsnNode insn, final Value value)
+ throws AnalyzerException
+ {
+ Value expected;
+ switch (insn.getOpcode()) {
+ case INEG:
+ case IINC:
+ case I2F:
+ case I2L:
+ case I2D:
+ case I2B:
+ case I2C:
+ case I2S:
+ case IFEQ:
+ case IFNE:
+ case IFLT:
+ case IFGE:
+ case IFGT:
+ case IFLE:
+ case TABLESWITCH:
+ case LOOKUPSWITCH:
+ case IRETURN:
+ case NEWARRAY:
+ case ANEWARRAY:
+ expected = BasicValue.INT_VALUE;
+ break;
+ case FNEG:
+ case F2I:
+ case F2L:
+ case F2D:
+ case FRETURN:
+ expected = BasicValue.FLOAT_VALUE;
+ break;
+ case LNEG:
+ case L2I:
+ case L2F:
+ case L2D:
+ case LRETURN:
+ expected = BasicValue.LONG_VALUE;
+ break;
+ case DNEG:
+ case D2I:
+ case D2F:
+ case D2L:
+ case DRETURN:
+ expected = BasicValue.DOUBLE_VALUE;
+ break;
+ case GETFIELD:
+ expected = newValue(Type.getObjectType(((FieldInsnNode) insn).owner));
+ break;
+ case CHECKCAST:
+ if (!((BasicValue) value).isReference()) {
+ throw new AnalyzerException(null,
+ "an object reference",
+ value);
+ }
+ return super.unaryOperation(insn, value);
+ case ARRAYLENGTH:
+ if (!isArrayValue(value)) {
+ throw new AnalyzerException(null,
+ "an array reference",
+ value);
+ }
+ return super.unaryOperation(insn, value);
+ case ARETURN:
+ case ATHROW:
+ case INSTANCEOF:
+ case MONITORENTER:
+ case MONITOREXIT:
+ case IFNULL:
+ case IFNONNULL:
+ if (!((BasicValue) value).isReference()) {
+ throw new AnalyzerException(null,
+ "an object reference",
+ value);
+ }
+ return super.unaryOperation(insn, value);
+ case PUTSTATIC:
+ expected = newValue(Type.getType(((FieldInsnNode) insn).desc));
+ break;
+ default:
+ throw new Error("Internal error.");
+ }
+ if (!isSubTypeOf(value, expected)) {
+ throw new AnalyzerException(null, expected, value);
+ }
+ return super.unaryOperation(insn, value);
+ }
+
+ public Value binaryOperation(
+ final AbstractInsnNode insn,
+ final Value value1,
+ final Value value2) throws AnalyzerException
+ {
+ Value expected1;
+ Value expected2;
+ switch (insn.getOpcode()) {
+ case IALOAD:
+ expected1 = newValue(Type.getType("[I"));
+ expected2 = BasicValue.INT_VALUE;
+ break;
+ case BALOAD:
+ if (isSubTypeOf(value1, newValue(Type.getType("[Z")))) {
+ expected1 = newValue(Type.getType("[Z"));
+ } else {
+ expected1 = newValue(Type.getType("[B"));
+ }
+ expected2 = BasicValue.INT_VALUE;
+ break;
+ case CALOAD:
+ expected1 = newValue(Type.getType("[C"));
+ expected2 = BasicValue.INT_VALUE;
+ break;
+ case SALOAD:
+ expected1 = newValue(Type.getType("[S"));
+ expected2 = BasicValue.INT_VALUE;
+ break;
+ case LALOAD:
+ expected1 = newValue(Type.getType("[J"));
+ expected2 = BasicValue.INT_VALUE;
+ break;
+ case FALOAD:
+ expected1 = newValue(Type.getType("[F"));
+ expected2 = BasicValue.INT_VALUE;
+ break;
+ case DALOAD:
+ expected1 = newValue(Type.getType("[D"));
+ expected2 = BasicValue.INT_VALUE;
+ break;
+ case AALOAD:
+ expected1 = newValue(Type.getType("[Ljava/lang/Object;"));
+ expected2 = BasicValue.INT_VALUE;
+ break;
+ case IADD:
+ case ISUB:
+ case IMUL:
+ case IDIV:
+ case IREM:
+ case ISHL:
+ case ISHR:
+ case IUSHR:
+ case IAND:
+ case IOR:
+ case IXOR:
+ case IF_ICMPEQ:
+ case IF_ICMPNE:
+ case IF_ICMPLT:
+ case IF_ICMPGE:
+ case IF_ICMPGT:
+ case IF_ICMPLE:
+ expected1 = BasicValue.INT_VALUE;
+ expected2 = BasicValue.INT_VALUE;
+ break;
+ case FADD:
+ case FSUB:
+ case FMUL:
+ case FDIV:
+ case FREM:
+ case FCMPL:
+ case FCMPG:
+ expected1 = BasicValue.FLOAT_VALUE;
+ expected2 = BasicValue.FLOAT_VALUE;
+ break;
+ case LADD:
+ case LSUB:
+ case LMUL:
+ case LDIV:
+ case LREM:
+ case LAND:
+ case LOR:
+ case LXOR:
+ case LCMP:
+ expected1 = BasicValue.LONG_VALUE;
+ expected2 = BasicValue.LONG_VALUE;
+ break;
+ case LSHL:
+ case LSHR:
+ case LUSHR:
+ expected1 = BasicValue.LONG_VALUE;
+ expected2 = BasicValue.INT_VALUE;
+ break;
+ case DADD:
+ case DSUB:
+ case DMUL:
+ case DDIV:
+ case DREM:
+ case DCMPL:
+ case DCMPG:
+ expected1 = BasicValue.DOUBLE_VALUE;
+ expected2 = BasicValue.DOUBLE_VALUE;
+ break;
+ case IF_ACMPEQ:
+ case IF_ACMPNE:
+ expected1 = BasicValue.REFERENCE_VALUE;
+ expected2 = BasicValue.REFERENCE_VALUE;
+ break;
+ case PUTFIELD:
+ FieldInsnNode fin = (FieldInsnNode) insn;
+ expected1 = newValue(Type.getObjectType(fin.owner));
+ expected2 = newValue(Type.getType(fin.desc));
+ break;
+ default:
+ throw new Error("Internal error.");
+ }
+ if (!isSubTypeOf(value1, expected1)) {
+ throw new AnalyzerException("First argument", expected1, value1);
+ } else if (!isSubTypeOf(value2, expected2)) {
+ throw new AnalyzerException("Second argument", expected2, value2);
+ }
+ if (insn.getOpcode() == AALOAD) {
+ return getElementValue(value1);
+ } else {
+ return super.binaryOperation(insn, value1, value2);
+ }
+ }
+
+ public Value ternaryOperation(
+ final AbstractInsnNode insn,
+ final Value value1,
+ final Value value2,
+ final Value value3) throws AnalyzerException
+ {
+ Value expected1;
+ Value expected3;
+ switch (insn.getOpcode()) {
+ case IASTORE:
+ expected1 = newValue(Type.getType("[I"));
+ expected3 = BasicValue.INT_VALUE;
+ break;
+ case BASTORE:
+ if (isSubTypeOf(value1, newValue(Type.getType("[Z")))) {
+ expected1 = newValue(Type.getType("[Z"));
+ } else {
+ expected1 = newValue(Type.getType("[B"));
+ }
+ expected3 = BasicValue.INT_VALUE;
+ break;
+ case CASTORE:
+ expected1 = newValue(Type.getType("[C"));
+ expected3 = BasicValue.INT_VALUE;
+ break;
+ case SASTORE:
+ expected1 = newValue(Type.getType("[S"));
+ expected3 = BasicValue.INT_VALUE;
+ break;
+ case LASTORE:
+ expected1 = newValue(Type.getType("[J"));
+ expected3 = BasicValue.LONG_VALUE;
+ break;
+ case FASTORE:
+ expected1 = newValue(Type.getType("[F"));
+ expected3 = BasicValue.FLOAT_VALUE;
+ break;
+ case DASTORE:
+ expected1 = newValue(Type.getType("[D"));
+ expected3 = BasicValue.DOUBLE_VALUE;
+ break;
+ case AASTORE:
+ expected1 = value1;
+ expected3 = BasicValue.REFERENCE_VALUE;
+ break;
+ default:
+ throw new Error("Internal error.");
+ }
+ if (!isSubTypeOf(value1, expected1)) {
+ throw new AnalyzerException("First argument", "a " + expected1
+ + " array reference", value1);
+ } else if (value2 != BasicValue.INT_VALUE) {
+ throw new AnalyzerException("Second argument",
+ BasicValue.INT_VALUE,
+ value2);
+ } else if (!isSubTypeOf(value3, expected3)) {
+ throw new AnalyzerException("Third argument", expected3, value3);
+ }
+ return null;
+ }
+
+ public Value naryOperation(final AbstractInsnNode insn, final List values)
+ throws AnalyzerException
+ {
+ int opcode = insn.getOpcode();
+ if (opcode == MULTIANEWARRAY) {
+ for (int i = 0; i < values.size(); ++i) {
+ if (values.get(i) != BasicValue.INT_VALUE) {
+ throw new AnalyzerException(null,
+ BasicValue.INT_VALUE,
+ (Value) values.get(i));
+ }
+ }
+ } else {
+ int i = 0;
+ int j = 0;
+ if (opcode != INVOKESTATIC) {
+ Type owner = Type.getObjectType(((MethodInsnNode) insn).owner);
+ if (!isSubTypeOf((Value) values.get(i++), newValue(owner))) {
+ throw new AnalyzerException("Method owner",
+ newValue(owner),
+ (Value) values.get(0));
+ }
+ }
+ Type[] args = Type.getArgumentTypes(((MethodInsnNode) insn).desc);
+ while (i < values.size()) {
+ Value expected = newValue(args[j++]);
+ Value encountered = (Value) values.get(i++);
+ if (!isSubTypeOf(encountered, expected)) {
+ throw new AnalyzerException("Argument " + j,
+ expected,
+ encountered);
+ }
+ }
+ }
+ return super.naryOperation(insn, values);
+ }
+
+ protected boolean isArrayValue(final Value value) {
+ return ((BasicValue) value).isReference();
+ }
+
+ protected Value getElementValue(final Value objectArrayValue)
+ throws AnalyzerException
+ {
+ return BasicValue.REFERENCE_VALUE;
+ }
+
+ protected boolean isSubTypeOf(final Value value, final Value expected) {
+ return value == expected;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/Frame.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/Frame.java
new file mode 100644
index 0000000..369fa61
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/Frame.java
@@ -0,0 +1,667 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.mockito.asm.Opcodes;
+import org.mockito.asm.Type;
+import org.mockito.asm.tree.AbstractInsnNode;
+import org.mockito.asm.tree.IincInsnNode;
+import org.mockito.asm.tree.MethodInsnNode;
+import org.mockito.asm.tree.MultiANewArrayInsnNode;
+import org.mockito.asm.tree.VarInsnNode;
+
+/**
+ * A symbolic execution stack frame. A stack frame contains a set of local
+ * variable slots, and an operand stack. Warning: long and double values are
+ * represented by <i>two</i> slots in local variables, and by <i>one</i> slot
+ * in the operand stack.
+ *
+ * @author Eric Bruneton
+ */
+public class Frame {
+
+ /**
+ * The local variables and operand stack of this frame.
+ */
+ private Value[] values;
+
+ /**
+ * The number of local variables of this frame.
+ */
+ private int locals;
+
+ /**
+ * The number of elements in the operand stack.
+ */
+ private int top;
+
+ /**
+ * Constructs a new frame with the given size.
+ *
+ * @param nLocals the maximum number of local variables of the frame.
+ * @param nStack the maximum stack size of the frame.
+ */
+ public Frame(final int nLocals, final int nStack) {
+ this.values = new Value[nLocals + nStack];
+ this.locals = nLocals;
+ }
+
+ /**
+ * Constructs a new frame that is identical to the given frame.
+ *
+ * @param src a frame.
+ */
+ public Frame(final Frame src) {
+ this(src.locals, src.values.length - src.locals);
+ init(src);
+ }
+
+ /**
+ * Copies the state of the given frame into this frame.
+ *
+ * @param src a frame.
+ * @return this frame.
+ */
+ public Frame init(final Frame src) {
+ System.arraycopy(src.values, 0, values, 0, values.length);
+ top = src.top;
+ return this;
+ }
+
+ /**
+ * Returns the maximum number of local variables of this frame.
+ *
+ * @return the maximum number of local variables of this frame.
+ */
+ public int getLocals() {
+ return locals;
+ }
+
+ /**
+ * Returns the value of the given local variable.
+ *
+ * @param i a local variable index.
+ * @return the value of the given local variable.
+ * @throws IndexOutOfBoundsException if the variable does not exist.
+ */
+ public Value getLocal(final int i) throws IndexOutOfBoundsException {
+ if (i >= locals) {
+ throw new IndexOutOfBoundsException("Trying to access an inexistant local variable");
+ }
+ return values[i];
+ }
+
+ /**
+ * Sets the value of the given local variable.
+ *
+ * @param i a local variable index.
+ * @param value the new value of this local variable.
+ * @throws IndexOutOfBoundsException if the variable does not exist.
+ */
+ public void setLocal(final int i, final Value value)
+ throws IndexOutOfBoundsException
+ {
+ if (i >= locals) {
+ throw new IndexOutOfBoundsException("Trying to access an inexistant local variable");
+ }
+ values[i] = value;
+ }
+
+ /**
+ * Returns the number of values in the operand stack of this frame. Long and
+ * double values are treated as single values.
+ *
+ * @return the number of values in the operand stack of this frame.
+ */
+ public int getStackSize() {
+ return top;
+ }
+
+ /**
+ * Returns the value of the given operand stack slot.
+ *
+ * @param i the index of an operand stack slot.
+ * @return the value of the given operand stack slot.
+ * @throws IndexOutOfBoundsException if the operand stack slot does not
+ * exist.
+ */
+ public Value getStack(final int i) throws IndexOutOfBoundsException {
+ return values[i + locals];
+ }
+
+ /**
+ * Clears the operand stack of this frame.
+ */
+ public void clearStack() {
+ top = 0;
+ }
+
+ /**
+ * Pops a value from the operand stack of this frame.
+ *
+ * @return the value that has been popped from the stack.
+ * @throws IndexOutOfBoundsException if the operand stack is empty.
+ */
+ public Value pop() throws IndexOutOfBoundsException {
+ if (top == 0) {
+ throw new IndexOutOfBoundsException("Cannot pop operand off an empty stack.");
+ }
+ return values[--top + locals];
+ }
+
+ /**
+ * Pushes a value into the operand stack of this frame.
+ *
+ * @param value the value that must be pushed into the stack.
+ * @throws IndexOutOfBoundsException if the operand stack is full.
+ */
+ public void push(final Value value) throws IndexOutOfBoundsException {
+ if (top + locals >= values.length) {
+ throw new IndexOutOfBoundsException("Insufficient maximum stack size.");
+ }
+ values[top++ + locals] = value;
+ }
+
+ public void execute(
+ final AbstractInsnNode insn,
+ final Interpreter interpreter) throws AnalyzerException
+ {
+ Value value1, value2, value3, value4;
+ List values;
+ int var;
+
+ switch (insn.getOpcode()) {
+ case Opcodes.NOP:
+ break;
+ case Opcodes.ACONST_NULL:
+ case Opcodes.ICONST_M1:
+ case Opcodes.ICONST_0:
+ case Opcodes.ICONST_1:
+ case Opcodes.ICONST_2:
+ case Opcodes.ICONST_3:
+ case Opcodes.ICONST_4:
+ case Opcodes.ICONST_5:
+ case Opcodes.LCONST_0:
+ case Opcodes.LCONST_1:
+ case Opcodes.FCONST_0:
+ case Opcodes.FCONST_1:
+ case Opcodes.FCONST_2:
+ case Opcodes.DCONST_0:
+ case Opcodes.DCONST_1:
+ case Opcodes.BIPUSH:
+ case Opcodes.SIPUSH:
+ case Opcodes.LDC:
+ push(interpreter.newOperation(insn));
+ break;
+ case Opcodes.ILOAD:
+ case Opcodes.LLOAD:
+ case Opcodes.FLOAD:
+ case Opcodes.DLOAD:
+ case Opcodes.ALOAD:
+ push(interpreter.copyOperation(insn,
+ getLocal(((VarInsnNode) insn).var)));
+ break;
+ case Opcodes.IALOAD:
+ case Opcodes.LALOAD:
+ case Opcodes.FALOAD:
+ case Opcodes.DALOAD:
+ case Opcodes.AALOAD:
+ case Opcodes.BALOAD:
+ case Opcodes.CALOAD:
+ case Opcodes.SALOAD:
+ value2 = pop();
+ value1 = pop();
+ push(interpreter.binaryOperation(insn, value1, value2));
+ break;
+ case Opcodes.ISTORE:
+ case Opcodes.LSTORE:
+ case Opcodes.FSTORE:
+ case Opcodes.DSTORE:
+ case Opcodes.ASTORE:
+ value1 = interpreter.copyOperation(insn, pop());
+ var = ((VarInsnNode) insn).var;
+ setLocal(var, value1);
+ if (value1.getSize() == 2) {
+ setLocal(var + 1, interpreter.newValue(null));
+ }
+ if (var > 0) {
+ Value local = getLocal(var - 1);
+ if (local != null && local.getSize() == 2) {
+ setLocal(var - 1, interpreter.newValue(null));
+ }
+ }
+ break;
+ case Opcodes.IASTORE:
+ case Opcodes.LASTORE:
+ case Opcodes.FASTORE:
+ case Opcodes.DASTORE:
+ case Opcodes.AASTORE:
+ case Opcodes.BASTORE:
+ case Opcodes.CASTORE:
+ case Opcodes.SASTORE:
+ value3 = pop();
+ value2 = pop();
+ value1 = pop();
+ interpreter.ternaryOperation(insn, value1, value2, value3);
+ break;
+ case Opcodes.POP:
+ if (pop().getSize() == 2) {
+ throw new AnalyzerException("Illegal use of POP");
+ }
+ break;
+ case Opcodes.POP2:
+ if (pop().getSize() == 1) {
+ if (pop().getSize() != 1) {
+ throw new AnalyzerException("Illegal use of POP2");
+ }
+ }
+ break;
+ case Opcodes.DUP:
+ value1 = pop();
+ if (value1.getSize() != 1) {
+ throw new AnalyzerException("Illegal use of DUP");
+ }
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ case Opcodes.DUP_X1:
+ value1 = pop();
+ value2 = pop();
+ if (value1.getSize() != 1 || value2.getSize() != 1) {
+ throw new AnalyzerException("Illegal use of DUP_X1");
+ }
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ case Opcodes.DUP_X2:
+ value1 = pop();
+ if (value1.getSize() == 1) {
+ value2 = pop();
+ if (value2.getSize() == 1) {
+ value3 = pop();
+ if (value3.getSize() == 1) {
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value3));
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ }
+ } else {
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ }
+ }
+ throw new AnalyzerException("Illegal use of DUP_X2");
+ case Opcodes.DUP2:
+ value1 = pop();
+ if (value1.getSize() == 1) {
+ value2 = pop();
+ if (value2.getSize() == 1) {
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ }
+ } else {
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ }
+ throw new AnalyzerException("Illegal use of DUP2");
+ case Opcodes.DUP2_X1:
+ value1 = pop();
+ if (value1.getSize() == 1) {
+ value2 = pop();
+ if (value2.getSize() == 1) {
+ value3 = pop();
+ if (value3.getSize() == 1) {
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value3));
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ }
+ }
+ } else {
+ value2 = pop();
+ if (value2.getSize() == 1) {
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ }
+ }
+ throw new AnalyzerException("Illegal use of DUP2_X1");
+ case Opcodes.DUP2_X2:
+ value1 = pop();
+ if (value1.getSize() == 1) {
+ value2 = pop();
+ if (value2.getSize() == 1) {
+ value3 = pop();
+ if (value3.getSize() == 1) {
+ value4 = pop();
+ if (value4.getSize() == 1) {
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value4));
+ push(interpreter.copyOperation(insn, value3));
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ }
+ } else {
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value3));
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ }
+ }
+ } else {
+ value2 = pop();
+ if (value2.getSize() == 1) {
+ value3 = pop();
+ if (value3.getSize() == 1) {
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value3));
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ }
+ } else {
+ push(interpreter.copyOperation(insn, value1));
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ }
+ }
+ throw new AnalyzerException("Illegal use of DUP2_X2");
+ case Opcodes.SWAP:
+ value2 = pop();
+ value1 = pop();
+ if (value1.getSize() != 1 || value2.getSize() != 1) {
+ throw new AnalyzerException("Illegal use of SWAP");
+ }
+ push(interpreter.copyOperation(insn, value2));
+ push(interpreter.copyOperation(insn, value1));
+ break;
+ case Opcodes.IADD:
+ case Opcodes.LADD:
+ case Opcodes.FADD:
+ case Opcodes.DADD:
+ case Opcodes.ISUB:
+ case Opcodes.LSUB:
+ case Opcodes.FSUB:
+ case Opcodes.DSUB:
+ case Opcodes.IMUL:
+ case Opcodes.LMUL:
+ case Opcodes.FMUL:
+ case Opcodes.DMUL:
+ case Opcodes.IDIV:
+ case Opcodes.LDIV:
+ case Opcodes.FDIV:
+ case Opcodes.DDIV:
+ case Opcodes.IREM:
+ case Opcodes.LREM:
+ case Opcodes.FREM:
+ case Opcodes.DREM:
+ value2 = pop();
+ value1 = pop();
+ push(interpreter.binaryOperation(insn, value1, value2));
+ break;
+ case Opcodes.INEG:
+ case Opcodes.LNEG:
+ case Opcodes.FNEG:
+ case Opcodes.DNEG:
+ push(interpreter.unaryOperation(insn, pop()));
+ break;
+ case Opcodes.ISHL:
+ case Opcodes.LSHL:
+ case Opcodes.ISHR:
+ case Opcodes.LSHR:
+ case Opcodes.IUSHR:
+ case Opcodes.LUSHR:
+ case Opcodes.IAND:
+ case Opcodes.LAND:
+ case Opcodes.IOR:
+ case Opcodes.LOR:
+ case Opcodes.IXOR:
+ case Opcodes.LXOR:
+ value2 = pop();
+ value1 = pop();
+ push(interpreter.binaryOperation(insn, value1, value2));
+ break;
+ case Opcodes.IINC:
+ var = ((IincInsnNode) insn).var;
+ setLocal(var, interpreter.unaryOperation(insn, getLocal(var)));
+ break;
+ case Opcodes.I2L:
+ case Opcodes.I2F:
+ case Opcodes.I2D:
+ case Opcodes.L2I:
+ case Opcodes.L2F:
+ case Opcodes.L2D:
+ case Opcodes.F2I:
+ case Opcodes.F2L:
+ case Opcodes.F2D:
+ case Opcodes.D2I:
+ case Opcodes.D2L:
+ case Opcodes.D2F:
+ case Opcodes.I2B:
+ case Opcodes.I2C:
+ case Opcodes.I2S:
+ push(interpreter.unaryOperation(insn, pop()));
+ break;
+ case Opcodes.LCMP:
+ case Opcodes.FCMPL:
+ case Opcodes.FCMPG:
+ case Opcodes.DCMPL:
+ case Opcodes.DCMPG:
+ value2 = pop();
+ value1 = pop();
+ push(interpreter.binaryOperation(insn, value1, value2));
+ break;
+ case Opcodes.IFEQ:
+ case Opcodes.IFNE:
+ case Opcodes.IFLT:
+ case Opcodes.IFGE:
+ case Opcodes.IFGT:
+ case Opcodes.IFLE:
+ interpreter.unaryOperation(insn, pop());
+ break;
+ case Opcodes.IF_ICMPEQ:
+ case Opcodes.IF_ICMPNE:
+ case Opcodes.IF_ICMPLT:
+ case Opcodes.IF_ICMPGE:
+ case Opcodes.IF_ICMPGT:
+ case Opcodes.IF_ICMPLE:
+ case Opcodes.IF_ACMPEQ:
+ case Opcodes.IF_ACMPNE:
+ value2 = pop();
+ value1 = pop();
+ interpreter.binaryOperation(insn, value1, value2);
+ break;
+ case Opcodes.GOTO:
+ break;
+ case Opcodes.JSR:
+ push(interpreter.newOperation(insn));
+ break;
+ case Opcodes.RET:
+ break;
+ case Opcodes.TABLESWITCH:
+ case Opcodes.LOOKUPSWITCH:
+ case Opcodes.IRETURN:
+ case Opcodes.LRETURN:
+ case Opcodes.FRETURN:
+ case Opcodes.DRETURN:
+ case Opcodes.ARETURN:
+ interpreter.unaryOperation(insn, pop());
+ break;
+ case Opcodes.RETURN:
+ break;
+ case Opcodes.GETSTATIC:
+ push(interpreter.newOperation(insn));
+ break;
+ case Opcodes.PUTSTATIC:
+ interpreter.unaryOperation(insn, pop());
+ break;
+ case Opcodes.GETFIELD:
+ push(interpreter.unaryOperation(insn, pop()));
+ break;
+ case Opcodes.PUTFIELD:
+ value2 = pop();
+ value1 = pop();
+ interpreter.binaryOperation(insn, value1, value2);
+ break;
+ case Opcodes.INVOKEVIRTUAL:
+ case Opcodes.INVOKESPECIAL:
+ case Opcodes.INVOKESTATIC:
+ case Opcodes.INVOKEINTERFACE:
+ values = new ArrayList();
+ String desc = ((MethodInsnNode) insn).desc;
+ for (int i = Type.getArgumentTypes(desc).length; i > 0; --i) {
+ values.add(0, pop());
+ }
+ if (insn.getOpcode() != Opcodes.INVOKESTATIC) {
+ values.add(0, pop());
+ }
+ if (Type.getReturnType(desc) == Type.VOID_TYPE) {
+ interpreter.naryOperation(insn, values);
+ } else {
+ push(interpreter.naryOperation(insn, values));
+ }
+ break;
+ case Opcodes.NEW:
+ push(interpreter.newOperation(insn));
+ break;
+ case Opcodes.NEWARRAY:
+ case Opcodes.ANEWARRAY:
+ case Opcodes.ARRAYLENGTH:
+ push(interpreter.unaryOperation(insn, pop()));
+ break;
+ case Opcodes.ATHROW:
+ interpreter.unaryOperation(insn, pop());
+ break;
+ case Opcodes.CHECKCAST:
+ case Opcodes.INSTANCEOF:
+ push(interpreter.unaryOperation(insn, pop()));
+ break;
+ case Opcodes.MONITORENTER:
+ case Opcodes.MONITOREXIT:
+ interpreter.unaryOperation(insn, pop());
+ break;
+ case Opcodes.MULTIANEWARRAY:
+ values = new ArrayList();
+ for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) {
+ values.add(0, pop());
+ }
+ push(interpreter.naryOperation(insn, values));
+ break;
+ case Opcodes.IFNULL:
+ case Opcodes.IFNONNULL:
+ interpreter.unaryOperation(insn, pop());
+ break;
+ default:
+ throw new RuntimeException("Illegal opcode");
+ }
+ }
+
+ /**
+ * Merges this frame with the given frame.
+ *
+ * @param frame a frame.
+ * @param interpreter the interpreter used to merge values.
+ * @return <tt>true</tt> if this frame has been changed as a result of the
+ * merge operation, or <tt>false</tt> otherwise.
+ * @throws AnalyzerException if the frames have incompatible sizes.
+ */
+ public boolean merge(final Frame frame, final Interpreter interpreter)
+ throws AnalyzerException
+ {
+ if (top != frame.top) {
+ throw new AnalyzerException("Incompatible stack heights");
+ }
+ boolean changes = false;
+ for (int i = 0; i < locals + top; ++i) {
+ Value v = interpreter.merge(values[i], frame.values[i]);
+ if (v != values[i]) {
+ values[i] = v;
+ changes |= true;
+ }
+ }
+ return changes;
+ }
+
+ /**
+ * Merges this frame with the given frame (case of a RET instruction).
+ *
+ * @param frame a frame
+ * @param access the local variables that have been accessed by the
+ * subroutine to which the RET instruction corresponds.
+ * @return <tt>true</tt> if this frame has been changed as a result of the
+ * merge operation, or <tt>false</tt> otherwise.
+ */
+ public boolean merge(final Frame frame, final boolean[] access) {
+ boolean changes = false;
+ for (int i = 0; i < locals; ++i) {
+ if (!access[i] && !values[i].equals(frame.values[i])) {
+ values[i] = frame.values[i];
+ changes = true;
+ }
+ }
+ return changes;
+ }
+
+ /**
+ * Returns a string representation of this frame.
+ *
+ * @return a string representation of this frame.
+ */
+ public String toString() {
+ StringBuffer b = new StringBuffer();
+ for (int i = 0; i < getLocals(); ++i) {
+ b.append(getLocal(i));
+ }
+ b.append(' ');
+ for (int i = 0; i < getStackSize(); ++i) {
+ b.append(getStack(i).toString());
+ }
+ return b.toString();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/Interpreter.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/Interpreter.java
new file mode 100644
index 0000000..ec503a0
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/Interpreter.java
@@ -0,0 +1,178 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+import java.util.List;
+
+import org.mockito.asm.Type;
+import org.mockito.asm.tree.AbstractInsnNode;
+
+/**
+ * A semantic bytecode interpreter. More precisely, this interpreter only
+ * manages the computation of values from other values: it does not manage the
+ * transfer of values to or from the stack, and to or from the local variables.
+ * This separation allows a generic bytecode {@link Analyzer} to work with
+ * various semantic interpreters, without needing to duplicate the code to
+ * simulate the transfer of values.
+ *
+ * @author Eric Bruneton
+ */
+public interface Interpreter {
+
+ /**
+ * Creates a new value that represents the given type.
+ *
+ * Called for method parameters (including <code>this</code>),
+ * exception handler variable and with <code>null</code> type
+ * for variables reserved by long and double types.
+ *
+ * @param type a primitive or reference type, or <tt>null</tt> to
+ * represent an uninitialized value.
+ * @return a value that represents the given type. The size of the returned
+ * value must be equal to the size of the given type.
+ */
+ Value newValue(Type type);
+
+ /**
+ * Interprets a bytecode instruction without arguments. This method is
+ * called for the following opcodes:
+ *
+ * ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4,
+ * ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0,
+ * DCONST_1, BIPUSH, SIPUSH, LDC, JSR, GETSTATIC, NEW
+ *
+ * @param insn the bytecode instruction to be interpreted.
+ * @return the result of the interpretation of the given instruction.
+ * @throws AnalyzerException if an error occured during the interpretation.
+ */
+ Value newOperation(AbstractInsnNode insn) throws AnalyzerException;
+
+ /**
+ * Interprets a bytecode instruction that moves a value on the stack or to
+ * or from local variables. This method is called for the following opcodes:
+ *
+ * ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE,
+ * ASTORE, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP
+ *
+ * @param insn the bytecode instruction to be interpreted.
+ * @param value the value that must be moved by the instruction.
+ * @return the result of the interpretation of the given instruction. The
+ * returned value must be <tt>equal</tt> to the given value.
+ * @throws AnalyzerException if an error occured during the interpretation.
+ */
+ Value copyOperation(AbstractInsnNode insn, Value value)
+ throws AnalyzerException;
+
+ /**
+ * Interprets a bytecode instruction with a single argument. This method is
+ * called for the following opcodes:
+ *
+ * INEG, LNEG, FNEG, DNEG, IINC, I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L,
+ * F2D, D2I, D2L, D2F, I2B, I2C, I2S, IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE,
+ * TABLESWITCH, LOOKUPSWITCH, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN,
+ * PUTSTATIC, GETFIELD, NEWARRAY, ANEWARRAY, ARRAYLENGTH, ATHROW, CHECKCAST,
+ * INSTANCEOF, MONITORENTER, MONITOREXIT, IFNULL, IFNONNULL
+ *
+ * @param insn the bytecode instruction to be interpreted.
+ * @param value the argument of the instruction to be interpreted.
+ * @return the result of the interpretation of the given instruction.
+ * @throws AnalyzerException if an error occured during the interpretation.
+ */
+ Value unaryOperation(AbstractInsnNode insn, Value value)
+ throws AnalyzerException;
+
+ /**
+ * Interprets a bytecode instruction with two arguments. This method is
+ * called for the following opcodes:
+ *
+ * IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IADD,
+ * LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV,
+ * LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, ISHL, LSHL, ISHR, LSHR, IUSHR,
+ * LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, LCMP, FCMPL, FCMPG, DCMPL,
+ * DCMPG, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE,
+ * IF_ACMPEQ, IF_ACMPNE, PUTFIELD
+ *
+ * @param insn the bytecode instruction to be interpreted.
+ * @param value1 the first argument of the instruction to be interpreted.
+ * @param value2 the second argument of the instruction to be interpreted.
+ * @return the result of the interpretation of the given instruction.
+ * @throws AnalyzerException if an error occured during the interpretation.
+ */
+ Value binaryOperation(AbstractInsnNode insn, Value value1, Value value2)
+ throws AnalyzerException;
+
+ /**
+ * Interprets a bytecode instruction with three arguments. This method is
+ * called for the following opcodes:
+ *
+ * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE
+ *
+ * @param insn the bytecode instruction to be interpreted.
+ * @param value1 the first argument of the instruction to be interpreted.
+ * @param value2 the second argument of the instruction to be interpreted.
+ * @param value3 the third argument of the instruction to be interpreted.
+ * @return the result of the interpretation of the given instruction.
+ * @throws AnalyzerException if an error occured during the interpretation.
+ */
+ Value ternaryOperation(
+ AbstractInsnNode insn,
+ Value value1,
+ Value value2,
+ Value value3) throws AnalyzerException;
+
+ /**
+ * Interprets a bytecode instruction with a variable number of arguments.
+ * This method is called for the following opcodes:
+ *
+ * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE,
+ * MULTIANEWARRAY
+ *
+ * @param insn the bytecode instruction to be interpreted.
+ * @param values the arguments of the instruction to be interpreted.
+ * @return the result of the interpretation of the given instruction.
+ * @throws AnalyzerException if an error occured during the interpretation.
+ */
+ Value naryOperation(AbstractInsnNode insn, List values)
+ throws AnalyzerException;
+
+ /**
+ * Merges two values. The merge operation must return a value that
+ * represents both values (for instance, if the two values are two types,
+ * the merged value must be a common super type of the two types. If the two
+ * values are integer intervals, the merged value must be an interval that
+ * contains the previous ones. Likewise for other types of values).
+ *
+ * @param v a value.
+ * @param w another value.
+ * @return the merged value. If the merged value is equal to <tt>v</tt>,
+ * this method <i>must</i> return <tt>v</tt>.
+ */
+ Value merge(Value v, Value w);
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/SimpleVerifier.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/SimpleVerifier.java
new file mode 100644
index 0000000..35f3ccd
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/SimpleVerifier.java
@@ -0,0 +1,284 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+import java.util.List;
+
+import org.mockito.asm.Type;
+
+/**
+ * An extended {@link BasicVerifier} that performs more precise verifications.
+ * This verifier computes exact class types, instead of using a single "object
+ * reference" type (as done in the {@link BasicVerifier}).
+ *
+ * @author Eric Bruneton
+ * @author Bing Ran
+ */
+public class SimpleVerifier extends BasicVerifier {
+
+ /**
+ * The class that is verified.
+ */
+ private final Type currentClass;
+
+ /**
+ * The super class of the class that is verified.
+ */
+ private final Type currentSuperClass;
+
+ /**
+ * The interfaces implemented by the class that is verified.
+ */
+ private final List currentClassInterfaces;
+
+ /**
+ * If the class that is verified is an interface.
+ */
+ private final boolean isInterface;
+
+ /**
+ * Constructs a new {@link SimpleVerifier}.
+ */
+ public SimpleVerifier() {
+ this(null, null, false);
+ }
+
+ /**
+ * Constructs a new {@link SimpleVerifier} to verify a specific class. This
+ * class will not be loaded into the JVM since it may be incorrect.
+ *
+ * @param currentClass the class that is verified.
+ * @param currentSuperClass the super class of the class that is verified.
+ * @param isInterface if the class that is verified is an interface.
+ */
+ public SimpleVerifier(
+ final Type currentClass,
+ final Type currentSuperClass,
+ final boolean isInterface)
+ {
+ this(currentClass, currentSuperClass, null, isInterface);
+ }
+
+ /**
+ * Constructs a new {@link SimpleVerifier} to verify a specific class. This
+ * class will not be loaded into the JVM since it may be incorrect.
+ *
+ * @param currentClass the class that is verified.
+ * @param currentSuperClass the super class of the class that is verified.
+ * @param currentClassInterfaces the interfaces implemented by the class
+ * that is verified.
+ * @param isInterface if the class that is verified is an interface.
+ */
+ public SimpleVerifier(
+ final Type currentClass,
+ final Type currentSuperClass,
+ final List currentClassInterfaces,
+ final boolean isInterface)
+ {
+ this.currentClass = currentClass;
+ this.currentSuperClass = currentSuperClass;
+ this.currentClassInterfaces = currentClassInterfaces;
+ this.isInterface = isInterface;
+ }
+
+ public Value newValue(final Type type) {
+ if (type == null) {
+ return BasicValue.UNINITIALIZED_VALUE;
+ }
+
+ boolean isArray = type.getSort() == Type.ARRAY;
+ if (isArray) {
+ switch (type.getElementType().getSort()) {
+ case Type.BOOLEAN:
+ case Type.CHAR:
+ case Type.BYTE:
+ case Type.SHORT:
+ return new BasicValue(type);
+ }
+ }
+
+ Value v = super.newValue(type);
+ if (v == BasicValue.REFERENCE_VALUE) {
+ if (isArray) {
+ v = newValue(type.getElementType());
+ String desc = ((BasicValue) v).getType().getDescriptor();
+ for (int i = 0; i < type.getDimensions(); ++i) {
+ desc = '[' + desc;
+ }
+ v = new BasicValue(Type.getType(desc));
+ } else {
+ v = new BasicValue(type);
+ }
+ }
+ return v;
+ }
+
+ protected boolean isArrayValue(final Value value) {
+ Type t = ((BasicValue) value).getType();
+ return t != null
+ && ("Lnull;".equals(t.getDescriptor()) || t.getSort() == Type.ARRAY);
+ }
+
+ protected Value getElementValue(final Value objectArrayValue)
+ throws AnalyzerException
+ {
+ Type arrayType = ((BasicValue) objectArrayValue).getType();
+ if (arrayType != null) {
+ if (arrayType.getSort() == Type.ARRAY) {
+ return newValue(Type.getType(arrayType.getDescriptor()
+ .substring(1)));
+ } else if ("Lnull;".equals(arrayType.getDescriptor())) {
+ return objectArrayValue;
+ }
+ }
+ throw new Error("Internal error");
+ }
+
+ protected boolean isSubTypeOf(final Value value, final Value expected) {
+ Type expectedType = ((BasicValue) expected).getType();
+ Type type = ((BasicValue) value).getType();
+ switch (expectedType.getSort()) {
+ case Type.INT:
+ case Type.FLOAT:
+ case Type.LONG:
+ case Type.DOUBLE:
+ return type == expectedType;
+ case Type.ARRAY:
+ case Type.OBJECT:
+ if ("Lnull;".equals(type.getDescriptor())) {
+ return true;
+ } else if (type.getSort() == Type.OBJECT
+ || type.getSort() == Type.ARRAY)
+ {
+ return isAssignableFrom(expectedType, type);
+ } else {
+ return false;
+ }
+ default:
+ throw new Error("Internal error");
+ }
+ }
+
+ public Value merge(final Value v, final Value w) {
+ if (!v.equals(w)) {
+ Type t = ((BasicValue) v).getType();
+ Type u = ((BasicValue) w).getType();
+ if (t != null
+ && (t.getSort() == Type.OBJECT || t.getSort() == Type.ARRAY))
+ {
+ if (u != null
+ && (u.getSort() == Type.OBJECT || u.getSort() == Type.ARRAY))
+ {
+ if ("Lnull;".equals(t.getDescriptor())) {
+ return w;
+ }
+ if ("Lnull;".equals(u.getDescriptor())) {
+ return v;
+ }
+ if (isAssignableFrom(t, u)) {
+ return v;
+ }
+ if (isAssignableFrom(u, t)) {
+ return w;
+ }
+ // TODO case of array classes of the same dimension
+ // TODO should we look also for a common super interface?
+ // problem: there may be several possible common super
+ // interfaces
+ do {
+ if (t == null || isInterface(t)) {
+ return BasicValue.REFERENCE_VALUE;
+ }
+ t = getSuperClass(t);
+ if (isAssignableFrom(t, u)) {
+ return newValue(t);
+ }
+ } while (true);
+ }
+ }
+ return BasicValue.UNINITIALIZED_VALUE;
+ }
+ return v;
+ }
+
+ protected boolean isInterface(final Type t) {
+ if (currentClass != null && t.equals(currentClass)) {
+ return isInterface;
+ }
+ return getClass(t).isInterface();
+ }
+
+ protected Type getSuperClass(final Type t) {
+ if (currentClass != null && t.equals(currentClass)) {
+ return currentSuperClass;
+ }
+ Class c = getClass(t).getSuperclass();
+ return c == null ? null : Type.getType(c);
+ }
+
+ protected boolean isAssignableFrom(final Type t, final Type u) {
+ if (t.equals(u)) {
+ return true;
+ }
+ if (currentClass != null && t.equals(currentClass)) {
+ if (getSuperClass(u) == null) {
+ return false;
+ } else {
+ return isAssignableFrom(t, getSuperClass(u));
+ }
+ }
+ if (currentClass != null && u.equals(currentClass)) {
+ if (isAssignableFrom(t, currentSuperClass)) {
+ return true;
+ }
+ if (currentClassInterfaces != null) {
+ for (int i = 0; i < currentClassInterfaces.size(); ++i) {
+ Type v = (Type) currentClassInterfaces.get(i);
+ if (isAssignableFrom(t, v)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ return getClass(t).isAssignableFrom(getClass(u));
+ }
+
+ protected Class getClass(final Type t) {
+ try {
+ if (t.getSort() == Type.ARRAY) {
+ return Class.forName(t.getDescriptor().replace('/', '.'));
+ }
+ return Class.forName(t.getClassName());
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/SmallSet.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/SmallSet.java
new file mode 100644
index 0000000..ed96155
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/SmallSet.java
@@ -0,0 +1,126 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+import java.util.AbstractSet;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A set of at most two elements.
+ *
+ * @author Eric Bruneton
+ */
+class SmallSet extends AbstractSet implements Iterator {
+
+ // if e1 is null, e2 must be null; otherwise e2 must be different from e1
+
+ Object e1, e2;
+
+ static final Set EMPTY_SET = new SmallSet(null, null);
+
+ SmallSet(final Object e1, final Object e2) {
+ this.e1 = e1;
+ this.e2 = e2;
+ }
+
+ // -------------------------------------------------------------------------
+ // Implementation of inherited abstract methods
+ // -------------------------------------------------------------------------
+
+ public Iterator iterator() {
+ return new SmallSet(e1, e2);
+ }
+
+ public int size() {
+ return e1 == null ? 0 : (e2 == null ? 1 : 2);
+ }
+
+ // -------------------------------------------------------------------------
+ // Implementation of the Iterator interface
+ // -------------------------------------------------------------------------
+
+ public boolean hasNext() {
+ return e1 != null;
+ }
+
+ public Object next() {
+ Object e = e1;
+ e1 = e2;
+ e2 = null;
+ return e;
+ }
+
+ public void remove() {
+ }
+
+ // -------------------------------------------------------------------------
+ // Utility methods
+ // -------------------------------------------------------------------------
+
+ Set union(final SmallSet s) {
+ if ((s.e1 == e1 && s.e2 == e2) || (s.e1 == e2 && s.e2 == e1)) {
+ return this; // if the two sets are equal, return this
+ }
+ if (s.e1 == null) {
+ return this; // if s is empty, return this
+ }
+ if (e1 == null) {
+ return s; // if this is empty, return s
+ }
+ if (s.e2 == null) { // s contains exactly one element
+ if (e2 == null) {
+ return new SmallSet(e1, s.e1); // necessarily e1 != s.e1
+ } else if (s.e1 == e1 || s.e1 == e2) { // s is included in this
+ return this;
+ }
+ }
+ if (e2 == null) { // this contains exactly one element
+ // if (s.e2 == null) { // cannot happen
+ // return new SmallSet(e1, s.e1); // necessarily e1 != s.e1
+ // } else
+ if (e1 == s.e1 || e1 == s.e2) { // this in included in s
+ return s;
+ }
+ }
+ // here we know that there are at least 3 distinct elements
+ HashSet r = new HashSet(4);
+ r.add(e1);
+ if (e2 != null) {
+ r.add(e2);
+ }
+ r.add(s.e1);
+ if (s.e2 != null) {
+ r.add(s.e2);
+ }
+ return r;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/SourceInterpreter.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/SourceInterpreter.java
new file mode 100644
index 0000000..1a9e59b
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/SourceInterpreter.java
@@ -0,0 +1,174 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.mockito.asm.Opcodes;
+import org.mockito.asm.Type;
+import org.mockito.asm.tree.AbstractInsnNode;
+import org.mockito.asm.tree.FieldInsnNode;
+import org.mockito.asm.tree.LdcInsnNode;
+import org.mockito.asm.tree.MethodInsnNode;
+
+/**
+ * An {@link Interpreter} for {@link SourceValue} values.
+ *
+ * @author Eric Bruneton
+ */
+public class SourceInterpreter implements Opcodes, Interpreter {
+
+ public Value newValue(final Type type) {
+ return new SourceValue(type == null ? 1 : type.getSize());
+ }
+
+ public Value newOperation(final AbstractInsnNode insn) {
+ int size;
+ switch (insn.getOpcode()) {
+ case LCONST_0:
+ case LCONST_1:
+ case DCONST_0:
+ case DCONST_1:
+ size = 2;
+ break;
+ case LDC:
+ Object cst = ((LdcInsnNode) insn).cst;
+ size = cst instanceof Long || cst instanceof Double ? 2 : 1;
+ break;
+ case GETSTATIC:
+ size = Type.getType(((FieldInsnNode) insn).desc).getSize();
+ break;
+ default:
+ size = 1;
+ }
+ return new SourceValue(size, insn);
+ }
+
+ public Value copyOperation(final AbstractInsnNode insn, final Value value) {
+ return new SourceValue(value.getSize(), insn);
+ }
+
+ public Value unaryOperation(final AbstractInsnNode insn, final Value value)
+ {
+ int size;
+ switch (insn.getOpcode()) {
+ case LNEG:
+ case DNEG:
+ case I2L:
+ case I2D:
+ case L2D:
+ case F2L:
+ case F2D:
+ case D2L:
+ size = 2;
+ break;
+ case GETFIELD:
+ size = Type.getType(((FieldInsnNode) insn).desc).getSize();
+ break;
+ default:
+ size = 1;
+ }
+ return new SourceValue(size, insn);
+ }
+
+ public Value binaryOperation(
+ final AbstractInsnNode insn,
+ final Value value1,
+ final Value value2)
+ {
+ int size;
+ switch (insn.getOpcode()) {
+ case LALOAD:
+ case DALOAD:
+ case LADD:
+ case DADD:
+ case LSUB:
+ case DSUB:
+ case LMUL:
+ case DMUL:
+ case LDIV:
+ case DDIV:
+ case LREM:
+ case DREM:
+ case LSHL:
+ case LSHR:
+ case LUSHR:
+ case LAND:
+ case LOR:
+ case LXOR:
+ size = 2;
+ break;
+ default:
+ size = 1;
+ }
+ return new SourceValue(size, insn);
+ }
+
+ public Value ternaryOperation(
+ final AbstractInsnNode insn,
+ final Value value1,
+ final Value value2,
+ final Value value3)
+ {
+ return new SourceValue(1, insn);
+ }
+
+ public Value naryOperation(final AbstractInsnNode insn, final List values) {
+ int size;
+ if (insn.getOpcode() == MULTIANEWARRAY) {
+ size = 1;
+ } else {
+ size = Type.getReturnType(((MethodInsnNode) insn).desc).getSize();
+ }
+ return new SourceValue(size, insn);
+ }
+
+ public Value merge(final Value v, final Value w) {
+ SourceValue dv = (SourceValue) v;
+ SourceValue dw = (SourceValue) w;
+ if (dv.insns instanceof SmallSet && dw.insns instanceof SmallSet) {
+ Set s = ((SmallSet) dv.insns).union((SmallSet) dw.insns);
+ if (s == dv.insns && dv.size == dw.size) {
+ return v;
+ } else {
+ return new SourceValue(Math.min(dv.size, dw.size), s);
+ }
+ }
+ if (dv.size != dw.size || !dv.insns.containsAll(dw.insns)) {
+ Set s = new HashSet();
+ s.addAll(dv.insns);
+ s.addAll(dw.insns);
+ return new SourceValue(Math.min(dv.size, dw.size), s);
+ }
+ return v;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/SourceValue.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/SourceValue.java
new file mode 100644
index 0000000..9fd3c4f
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/SourceValue.java
@@ -0,0 +1,95 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+import java.util.Set;
+
+import org.mockito.asm.tree.AbstractInsnNode;
+
+/**
+ * A {@link Value} that is represented by its type in a two types type system.
+ * This type system distinguishes the ONEWORD and TWOWORDS types.
+ *
+ * @author Eric Bruneton
+ */
+public class SourceValue implements Value {
+
+ /**
+ * The size of this value.
+ */
+ public final int size;
+
+ /**
+ * The instructions that can produce this value. For example, for the Java
+ * code below, the instructions that can produce the value of <tt>i</tt>
+ * at line 5 are the txo ISTORE instructions at line 1 and 3:
+ *
+ * <pre>
+ * 1: i = 0;
+ * 2: if (...) {
+ * 3: i = 1;
+ * 4: }
+ * 5: return i;
+ * </pre>
+ *
+ * This field is a set of {@link AbstractInsnNode} objects.
+ */
+ public final Set insns;
+
+ public SourceValue(final int size) {
+ this(size, SmallSet.EMPTY_SET);
+ }
+
+ public SourceValue(final int size, final AbstractInsnNode insn) {
+ this.size = size;
+ this.insns = new SmallSet(insn, null);
+ }
+
+ public SourceValue(final int size, final Set insns) {
+ this.size = size;
+ this.insns = insns;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public boolean equals(final Object value) {
+ if (!(value instanceof SourceValue)) {
+ return false;
+ }
+ SourceValue v = (SourceValue) value;
+ return size == v.size && insns.equals(v.insns);
+ }
+
+ public int hashCode() {
+ return insns.hashCode();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/Subroutine.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/Subroutine.java
new file mode 100644
index 0000000..d0ce432
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/Subroutine.java
@@ -0,0 +1,93 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.mockito.asm.tree.JumpInsnNode;
+import org.mockito.asm.tree.LabelNode;
+
+/**
+ * A method subroutine (corresponds to a JSR instruction).
+ *
+ * @author Eric Bruneton
+ */
+class Subroutine {
+
+ LabelNode start;
+
+ boolean[] access;
+
+ List callers;
+
+ private Subroutine() {
+ }
+
+ Subroutine(
+ final LabelNode start,
+ final int maxLocals,
+ final JumpInsnNode caller)
+ {
+ this.start = start;
+ this.access = new boolean[maxLocals];
+ this.callers = new ArrayList();
+ callers.add(caller);
+ }
+
+ public Subroutine copy() {
+ Subroutine result = new Subroutine();
+ result.start = start;
+ result.access = new boolean[access.length];
+ System.arraycopy(access, 0, result.access, 0, access.length);
+ result.callers = new ArrayList(callers);
+ return result;
+ }
+
+ public boolean merge(final Subroutine subroutine) throws AnalyzerException {
+ boolean changes = false;
+ for (int i = 0; i < access.length; ++i) {
+ if (subroutine.access[i] && !access[i]) {
+ access[i] = true;
+ changes = true;
+ }
+ }
+ if (subroutine.start == start) {
+ for (int i = 0; i < subroutine.callers.size(); ++i) {
+ Object caller = subroutine.callers.get(i);
+ if (!callers.contains(caller)) {
+ callers.add(caller);
+ changes = true;
+ }
+ }
+ }
+ return changes;
+ }
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/Value.java b/cglib-and-asm/src/org/mockito/asm/tree/analysis/Value.java
new file mode 100644
index 0000000..83d3017
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/Value.java
@@ -0,0 +1,45 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.tree.analysis;
+
+/**
+ * An immutable symbolic value for semantic interpretation of bytecode.
+ *
+ * @author Eric Bruneton
+ */
+public interface Value {
+
+ /**
+ * Returns the size of this value in words.
+ *
+ * @return either 1 or 2.
+ */
+ int getSize();
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/analysis/package.html b/cglib-and-asm/src/org/mockito/asm/tree/analysis/package.html
new file mode 100644
index 0000000..46b6c26
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/analysis/package.html
@@ -0,0 +1,67 @@
+<html>
+<!--
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<body>
+
+<p>
+Provides a framework for static code analysis based on the asm.tree package.
+</p>
+
+<p>
+Basic usage:
+</p>
+
+<pre>
+ClassReader cr = new ClassReader(bytecode);
+ClassNode cn = new ClassNode();
+cr.accept(cn, ClassReader.SKIP_DEBUG);
+
+List methods = cn.methods;
+for (int i = 0; i < methods.size(); ++i) {
+ MethodNode method = (MethodNode) methods.get(i);
+ if (method.instructions.size() > 0) {
+ Analyzer a = new Analyzer(new BasicInterpreter());
+ a.analyze(cn.name, method);
+ Frame[] frames = a.getFrames();
+ // Elements of the frames arrray now contains info for each instruction
+ // from the analyzed method. BasicInterpreter creates BasicValue, that
+ // is using simplified type system that distinguishes the UNINITIALZED,
+ // INT, FLOAT, LONG, DOUBLE, REFERENCE and RETURNADDRESS types.
+ ...
+ }
+}
+</pre>
+
+<p>
+@since ASM 1.4.3
+</p>
+
+</body>
+</html>
diff --git a/cglib-and-asm/src/org/mockito/asm/tree/package.html b/cglib-and-asm/src/org/mockito/asm/tree/package.html
new file mode 100644
index 0000000..92ea013
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/tree/package.html
@@ -0,0 +1,192 @@
+<html>
+<!--
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<body>
+
+<p>
+Provides an ASM visitor that constructs a tree representation of the
+classes it visits. This class adapter can be useful to implement "complex"
+class manipulation operations, i.e., operations that would be very hard to
+implement without using a tree representation (such as optimizing the number
+of local variables used by a method).
+</p>
+
+<p>
+However, this class adapter has a cost: it makes ASM bigger and slower. Indeed
+it requires more than twenty new classes, and multiplies the time needed to
+transform a class by almost two (it is almost two times faster to read, "modify"
+and write a class with a ClassAdapter than with a ClassNode). This is why
+this package is bundled in an optional <tt>asm-tree.jar</tt> library that
+is separated from (but requires) the <tt>asm.jar</tt> library, which contains
+the core ASM framework. This is also why <i><font color="red">it is recommended
+not to use this class adapter when it is possible</font></i>.
+</p>
+
+<p>
+The root class is the ClassNode, that can be created from existing bytecode. For example:
+</p>
+
+<pre>
+ ClassReader cr = new ClassReader(source);
+ ClassNode cn = new ClassNode();
+ cr.accept(cn, true);
+</pre>
+
+<p>
+Now content of ClassNode can be modified and then
+serialized back into bytecode:
+</p>
+
+<pre>
+ ClassWriter cw = new ClassWriter(true);
+ cn.accept(cw);
+</pre>
+
+<p>
+Using simple ClassAdapter it is possible to create MethodNode instances per-method.
+In this example MethodNode is acting as a buffer that is flushed out at visitEnd() call:
+</p>
+
+<pre>
+ ClassReader cr = new ClassReader(source);
+ ClassWriter cw = new ClassWriter();
+ ClassAdapter ca = new ClassAdapter(cw) {
+ public MethodVisitor visitMethod(int access, String name,
+ String desc, String signature, String[] exceptions) {
+ final MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
+ MethodNode mn = new MethodNode(access, name, desc, signature, exceptions) {
+ public void visitEnd() {
+ // transform or analyze method code using tree API
+ accept(mv);
+ }
+ };
+ }
+ };
+ cr.accept(ca, true);
+</pre>
+
+<p>
+Several strategies can be used to construct method code from scratch. The first
+option is to create a MethodNode, and then create XXXInsnNode instances and
+add them to the instructions list:
+</p>
+
+<pre>
+MethodNode m = new MethodNode(...);
+m.instructions.add(new VarInsnNode(ALOAD, 0));
+...
+</pre>
+
+<p>
+Alternatively, you can use the fact that MethodNode is a MethodVisitor, and use
+that to create the XXXInsnNode and add them to the instructions list through
+the standard MethodVisitor interface:
+</p>
+
+<pre>
+MethodNode m = new MethodNode(...);
+m.visitVarInsn(ALOAD, 0);
+...
+</pre>
+
+<p>
+If you cannot generate all the instructions in sequential order, i.e. if you
+need to save some pointer in the instruction list and then insert instructions
+at that place after other instructions have been generated, you can use InsnList
+methods insert() and insertBefore() to insert instructions at saved pointer.
+</p>
+
+<pre>
+MethodNode m = new MethodNode(...);
+m.visitVarInsn(ALOAD, 0);
+AbstractInsnNode ptr = m.instructions.getLast();
+m.visitVarInsn(ALOAD, 1);
+// inserts an instruction between ALOAD 0 and ALOAD 1
+m.instructions.insert(ptr, new VarInsnNode(ALOAD, 0));
+...
+</pre>
+
+<p>
+If you need to insert instructions while iterating over an existing instruction
+list, you can also use several strategies. The first one is to use a
+ListIterator over the instruction list:
+</p>
+
+<pre>
+ListIterator it = m.instructions.iterator();
+while (it.hasNext()) {
+ AbstractInsnNode n = (AbstractInsnNode) it.next();
+ if (...) {
+ it.add(new VarInsnNode(ALOAD, 0));
+ }
+}
+</pre>
+
+<p>
+It is also possible to convert instruction list into the array and iterate trough
+array elements:
+</p>
+
+<pre>
+AbstractInsnNode[] insns = m.instructions.toArray();
+for(int i = 0; i&lt;insns.length; i++) {
+ AbstractInsnNode n = insns[i];
+ if (...) {
+ m.instructions.insert(n, new VarInsnNode(ALOAD, 0));
+ }
+}
+</pre>
+
+<p>
+If you want to insert these instructions through the MethodVisitor interface,
+you can use another instance of MethodNode as a MethodVisitor and then
+insert instructions collected by that instance into the instruction list.
+For example:
+</p>
+
+<pre>
+AbstractInsnNode[] insns = m.instructions.toArray();
+for(int i = 0; i&lt;insns.length; i++) {
+ AbstractInsnNode n = insns[i];
+ if (...) {
+ MethodNode mn = new MethodNode();
+ mn.visitVarInsn(ALOAD, 0);
+ mn.visitVarInsn(ALOAD, 1);
+ m.instructions.insert(n, mn.instructions);
+ }
+}
+</pre>
+
+<p>
+@since ASM 1.3.3
+</p>
+
+</body>
+</html>
diff --git a/cglib-and-asm/src/org/mockito/asm/util/ASMifiable.java b/cglib-and-asm/src/org/mockito/asm/util/ASMifiable.java
new file mode 100644
index 0000000..c21842e
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/ASMifiable.java
@@ -0,0 +1,53 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import java.util.Map;
+
+/**
+ * An attribute that can print the ASM code to create an equivalent attribute.
+ *
+ * Implementation should print the ASM code that generates attribute data
+ * structures for current attribute state.
+ *
+ * @author Eugene Kuleshov
+ */
+public interface ASMifiable {
+
+ /**
+ * Prints the ASM code to create an attribute equal to this attribute.
+ *
+ * @param buf A buffer used for printing Java code.
+ * @param varName name of the variable in a printed code used to store
+ * attribute instance.
+ * @param labelNames map of label instances to their names.
+ */
+ void asmify(StringBuffer buf, String varName, Map labelNames);
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/ASMifierAbstractVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/ASMifierAbstractVisitor.java
new file mode 100644
index 0000000..7640535
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/ASMifierAbstractVisitor.java
@@ -0,0 +1,222 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Attribute;
+import org.mockito.asm.Type;
+
+import java.util.Map;
+
+/**
+ * An abstract ASMifier visitor.
+ *
+ * @author Eric Bruneton
+ */
+public class ASMifierAbstractVisitor extends AbstractVisitor {
+
+ /**
+ * The name of the variable for this visitor in the produced code.
+ */
+ protected String name;
+
+ /**
+ * The label names. This map associates String values to Label keys. It is
+ * used only in ASMifierMethodVisitor.
+ */
+ Map labelNames;
+
+ /**
+ * Constructs a new {@link ASMifierAbstractVisitor}.
+ *
+ * @param name the name of the variable for this visitor in the produced
+ * code.
+ */
+ protected ASMifierAbstractVisitor(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Prints the ASM code that generates the given annotation.
+ *
+ * @param desc the class descriptor of the annotation class.
+ * @param visible <tt>true</tt> if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values.
+ */
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ buf.setLength(0);
+ buf.append("{\n")
+ .append("av0 = ")
+ .append(name)
+ .append(".visitAnnotation(");
+ appendConstant(desc);
+ buf.append(", ").append(visible).append(");\n");
+ text.add(buf.toString());
+ ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
+ text.add(av.getText());
+ text.add("}\n");
+ return av;
+ }
+
+ /**
+ * Prints the ASM code that generates the given attribute.
+ *
+ * @param attr an attribute.
+ */
+ public void visitAttribute(final Attribute attr) {
+ buf.setLength(0);
+ buf.append("// ATTRIBUTE ").append(attr.type).append('\n');
+ if (attr instanceof ASMifiable) {
+ buf.append("{\n");
+ ((ASMifiable) attr).asmify(buf, "attr", labelNames);
+ buf.append(name).append(".visitAttribute(attr);\n");
+ buf.append("}\n");
+ }
+ text.add(buf.toString());
+ }
+
+ /**
+ * Prints the ASM code to end the visit.
+ */
+ public void visitEnd() {
+ buf.setLength(0);
+ buf.append(name).append(".visitEnd();\n");
+ text.add(buf.toString());
+ }
+
+ /**
+ * Appends a string representation of the given constant to the given
+ * buffer.
+ *
+ * @param cst an {@link Integer}, {@link Float}, {@link Long},
+ * {@link Double} or {@link String} object. May be <tt>null</tt>.
+ */
+ void appendConstant(final Object cst) {
+ appendConstant(buf, cst);
+ }
+
+ /**
+ * Appends a string representation of the given constant to the given
+ * buffer.
+ *
+ * @param buf a string buffer.
+ * @param cst an {@link Integer}, {@link Float}, {@link Long},
+ * {@link Double} or {@link String} object. May be <tt>null</tt>.
+ */
+ static void appendConstant(final StringBuffer buf, final Object cst) {
+ if (cst == null) {
+ buf.append("null");
+ } else if (cst instanceof String) {
+ appendString(buf, (String) cst);
+ } else if (cst instanceof Type) {
+ buf.append("Type.getType(\"");
+ buf.append(((Type) cst).getDescriptor());
+ buf.append("\")");
+ } else if (cst instanceof Byte) {
+ buf.append("new Byte((byte)").append(cst).append(')');
+ } else if (cst instanceof Boolean) {
+ buf.append(((Boolean) cst).booleanValue() ? "Boolean.TRUE" : "Boolean.FALSE");
+ } else if (cst instanceof Short) {
+ buf.append("new Short((short)").append(cst).append(')');
+ } else if (cst instanceof Character) {
+ int c = ((Character) cst).charValue();
+ buf.append("new Character((char)").append(c).append(')');
+ } else if (cst instanceof Integer) {
+ buf.append("new Integer(").append(cst).append(')');
+ } else if (cst instanceof Float) {
+ buf.append("new Float(\"").append(cst).append("\")");
+ } else if (cst instanceof Long) {
+ buf.append("new Long(").append(cst).append("L)");
+ } else if (cst instanceof Double) {
+ buf.append("new Double(\"").append(cst).append("\")");
+ } else if (cst instanceof byte[]) {
+ byte[] v = (byte[]) cst;
+ buf.append("new byte[] {");
+ for (int i = 0; i < v.length; i++) {
+ buf.append(i == 0 ? "" : ",").append(v[i]);
+ }
+ buf.append('}');
+ } else if (cst instanceof boolean[]) {
+ boolean[] v = (boolean[]) cst;
+ buf.append("new boolean[] {");
+ for (int i = 0; i < v.length; i++) {
+ buf.append(i == 0 ? "" : ",").append(v[i]);
+ }
+ buf.append('}');
+ } else if (cst instanceof short[]) {
+ short[] v = (short[]) cst;
+ buf.append("new short[] {");
+ for (int i = 0; i < v.length; i++) {
+ buf.append(i == 0 ? "" : ",").append("(short)").append(v[i]);
+ }
+ buf.append('}');
+ } else if (cst instanceof char[]) {
+ char[] v = (char[]) cst;
+ buf.append("new char[] {");
+ for (int i = 0; i < v.length; i++) {
+ buf.append(i == 0 ? "" : ",")
+ .append("(char)")
+ .append((int) v[i]);
+ }
+ buf.append('}');
+ } else if (cst instanceof int[]) {
+ int[] v = (int[]) cst;
+ buf.append("new int[] {");
+ for (int i = 0; i < v.length; i++) {
+ buf.append(i == 0 ? "" : ",").append(v[i]);
+ }
+ buf.append('}');
+ } else if (cst instanceof long[]) {
+ long[] v = (long[]) cst;
+ buf.append("new long[] {");
+ for (int i = 0; i < v.length; i++) {
+ buf.append(i == 0 ? "" : ",").append(v[i]).append('L');
+ }
+ buf.append('}');
+ } else if (cst instanceof float[]) {
+ float[] v = (float[]) cst;
+ buf.append("new float[] {");
+ for (int i = 0; i < v.length; i++) {
+ buf.append(i == 0 ? "" : ",").append(v[i]).append('f');
+ }
+ buf.append('}');
+ } else if (cst instanceof double[]) {
+ double[] v = (double[]) cst;
+ buf.append("new double[] {");
+ for (int i = 0; i < v.length; i++) {
+ buf.append(i == 0 ? "" : ",").append(v[i]).append('d');
+ }
+ buf.append('}');
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/ASMifierAnnotationVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/ASMifierAnnotationVisitor.java
new file mode 100644
index 0000000..dc5e01e
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/ASMifierAnnotationVisitor.java
@@ -0,0 +1,127 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.AnnotationVisitor;
+
+/**
+ * An {@link AnnotationVisitor} that prints the ASM code that generates the
+ * annotations it visits.
+ *
+ * @author Eric Bruneton
+ */
+public class ASMifierAnnotationVisitor extends AbstractVisitor implements
+ AnnotationVisitor
+{
+
+ /**
+ * Identifier of the annotation visitor variable in the produced code.
+ */
+ protected final int id;
+
+ /**
+ * Constructs a new {@link ASMifierAnnotationVisitor}.
+ *
+ * @param id identifier of the annotation visitor variable in the produced
+ * code.
+ */
+ public ASMifierAnnotationVisitor(final int id) {
+ this.id = id;
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the AnnotationVisitor interface
+ // ------------------------------------------------------------------------
+
+ public void visit(final String name, final Object value) {
+ buf.setLength(0);
+ buf.append("av").append(id).append(".visit(");
+ ASMifierAbstractVisitor.appendConstant(buf, name);
+ buf.append(", ");
+ ASMifierAbstractVisitor.appendConstant(buf, value);
+ buf.append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitEnum(
+ final String name,
+ final String desc,
+ final String value)
+ {
+ buf.setLength(0);
+ buf.append("av").append(id).append(".visitEnum(");
+ ASMifierAbstractVisitor.appendConstant(buf, name);
+ buf.append(", ");
+ ASMifierAbstractVisitor.appendConstant(buf, desc);
+ buf.append(", ");
+ ASMifierAbstractVisitor.appendConstant(buf, value);
+ buf.append(");\n");
+ text.add(buf.toString());
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String name,
+ final String desc)
+ {
+ buf.setLength(0);
+ buf.append("{\n");
+ buf.append("AnnotationVisitor av").append(id + 1).append(" = av");
+ buf.append(id).append(".visitAnnotation(");
+ ASMifierAbstractVisitor.appendConstant(buf, name);
+ buf.append(", ");
+ ASMifierAbstractVisitor.appendConstant(buf, desc);
+ buf.append(");\n");
+ text.add(buf.toString());
+ ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(id + 1);
+ text.add(av.getText());
+ text.add("}\n");
+ return av;
+ }
+
+ public AnnotationVisitor visitArray(final String name) {
+ buf.setLength(0);
+ buf.append("{\n");
+ buf.append("AnnotationVisitor av").append(id + 1).append(" = av");
+ buf.append(id).append(".visitArray(");
+ ASMifierAbstractVisitor.appendConstant(buf, name);
+ buf.append(");\n");
+ text.add(buf.toString());
+ ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(id + 1);
+ text.add(av.getText());
+ text.add("}\n");
+ return av;
+ }
+
+ public void visitEnd() {
+ buf.setLength(0);
+ buf.append("av").append(id).append(".visitEnd();\n");
+ text.add(buf.toString());
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/ASMifierClassVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/ASMifierClassVisitor.java
new file mode 100644
index 0000000..abf4a59
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/ASMifierClassVisitor.java
@@ -0,0 +1,575 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import java.io.FileInputStream;
+import java.io.PrintWriter;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.ClassReader;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.FieldVisitor;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+
+/**
+ * A {@link ClassVisitor} that prints the ASM code that generates the classes it
+ * visits. This class visitor can be used to quickly write ASM code to generate
+ * some given bytecode: <ul> <li>write the Java source code equivalent to the
+ * bytecode you want to generate;</li> <li>compile it with <tt>javac</tt>;</li>
+ * <li>make a {@link ASMifierClassVisitor} visit this compiled class (see the
+ * {@link #main main} method);</li> <li>edit the generated source code, if
+ * necessary.</li> </ul> The source code printed when visiting the
+ * <tt>Hello</tt> class is the following: <p> <blockquote>
+ *
+ * <pre>
+ * import org.mockito.asm.*;
+ *
+ * public class HelloDump implements Opcodes {
+ *
+ * public static byte[] dump() throws Exception {
+ *
+ * ClassWriter cw = new ClassWriter(0);
+ * FieldVisitor fv;
+ * MethodVisitor mv;
+ * AnnotationVisitor av0;
+ *
+ * cw.visit(49,
+ * ACC_PUBLIC + ACC_SUPER,
+ * &quot;Hello&quot;,
+ * null,
+ * &quot;java/lang/Object&quot;,
+ * null);
+ *
+ * cw.visitSource(&quot;Hello.java&quot;, null);
+ *
+ * {
+ * mv = cw.visitMethod(ACC_PUBLIC, &quot;&lt;init&gt;&quot;, &quot;()V&quot;, null, null);
+ * mv.visitVarInsn(ALOAD, 0);
+ * mv.visitMethodInsn(INVOKESPECIAL,
+ * &quot;java/lang/Object&quot;,
+ * &quot;&lt;init&gt;&quot;,
+ * &quot;()V&quot;);
+ * mv.visitInsn(RETURN);
+ * mv.visitMaxs(1, 1);
+ * mv.visitEnd();
+ * }
+ * {
+ * mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
+ * &quot;main&quot;,
+ * &quot;([Ljava/lang/String;)V&quot;,
+ * null,
+ * null);
+ * mv.visitFieldInsn(GETSTATIC,
+ * &quot;java/lang/System&quot;,
+ * &quot;out&quot;,
+ * &quot;Ljava/io/PrintStream;&quot;);
+ * mv.visitLdcInsn(&quot;hello&quot;);
+ * mv.visitMethodInsn(INVOKEVIRTUAL,
+ * &quot;java/io/PrintStream&quot;,
+ * &quot;println&quot;,
+ * &quot;(Ljava/lang/String;)V&quot;);
+ * mv.visitInsn(RETURN);
+ * mv.visitMaxs(2, 1);
+ * mv.visitEnd();
+ * }
+ * cw.visitEnd();
+ *
+ * return cw.toByteArray();
+ * }
+ * }
+ *
+ * </pre>
+ *
+ * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote>
+ *
+ * <pre>
+ * public class Hello {
+ *
+ * public static void main(String[] args) {
+ * System.out.println(&quot;hello&quot;);
+ * }
+ * }
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class ASMifierClassVisitor extends ASMifierAbstractVisitor implements
+ ClassVisitor
+{
+
+ /**
+ * Pseudo access flag used to distinguish class access flags.
+ */
+ private static final int ACCESS_CLASS = 262144;
+
+ /**
+ * Pseudo access flag used to distinguish field access flags.
+ */
+ private static final int ACCESS_FIELD = 524288;
+
+ /**
+ * Pseudo access flag used to distinguish inner class flags.
+ */
+ private static final int ACCESS_INNER = 1048576;
+
+ /**
+ * The print writer to be used to print the class.
+ */
+ protected final PrintWriter pw;
+
+ /**
+ * Prints the ASM source code to generate the given class to the standard
+ * output. <p> Usage: ASMifierClassVisitor [-debug] &lt;fully qualified
+ * class name or class file name&gt;
+ *
+ * @param args the command line arguments.
+ *
+ * @throws Exception if the class cannot be found, or if an IO exception
+ * occurs.
+ */
+ public static void main(final String[] args) throws Exception {
+ int i = 0;
+ int flags = ClassReader.SKIP_DEBUG;
+
+ boolean ok = true;
+ if (args.length < 1 || args.length > 2) {
+ ok = false;
+ }
+ if (ok && "-debug".equals(args[0])) {
+ i = 1;
+ flags = 0;
+ if (args.length != 2) {
+ ok = false;
+ }
+ }
+ if (!ok) {
+ System.err.println("Prints the ASM code to generate the given class.");
+ System.err.println("Usage: ASMifierClassVisitor [-debug] "
+ + "<fully qualified class name or class file name>");
+ return;
+ }
+ ClassReader cr;
+ if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
+ || args[i].indexOf('/') > -1)
+ {
+ cr = new ClassReader(new FileInputStream(args[i]));
+ } else {
+ cr = new ClassReader(args[i]);
+ }
+ cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)),
+ getDefaultAttributes(),
+ flags);
+ }
+
+ /**
+ * Constructs a new {@link ASMifierClassVisitor} object.
+ *
+ * @param pw the print writer to be used to print the class.
+ */
+ public ASMifierClassVisitor(final PrintWriter pw) {
+ super("cw");
+ this.pw = pw;
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the ClassVisitor interface
+ // ------------------------------------------------------------------------
+
+ public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces)
+ {
+ String simpleName;
+ int n = name.lastIndexOf('/');
+ if (n == -1) {
+ simpleName = name;
+ } else {
+ text.add("package asm." + name.substring(0, n).replace('/', '.')
+ + ";\n");
+ simpleName = name.substring(n + 1);
+ }
+ text.add("import java.util.*;\n");
+ text.add("import org.mockito.asm.*;\n");
+ text.add("import org.mockito.asm.attrs.*;\n");
+ text.add("public class " + simpleName + "Dump implements Opcodes {\n\n");
+ text.add("public static byte[] dump () throws Exception {\n\n");
+ text.add("ClassWriter cw = new ClassWriter(0);\n");
+ text.add("FieldVisitor fv;\n");
+ text.add("MethodVisitor mv;\n");
+ text.add("AnnotationVisitor av0;\n\n");
+
+ buf.setLength(0);
+ buf.append("cw.visit(");
+ switch (version) {
+ case Opcodes.V1_1:
+ buf.append("V1_1");
+ break;
+ case Opcodes.V1_2:
+ buf.append("V1_2");
+ break;
+ case Opcodes.V1_3:
+ buf.append("V1_3");
+ break;
+ case Opcodes.V1_4:
+ buf.append("V1_4");
+ break;
+ case Opcodes.V1_5:
+ buf.append("V1_5");
+ break;
+ case Opcodes.V1_6:
+ buf.append("V1_6");
+ break;
+ default:
+ buf.append(version);
+ break;
+ }
+ buf.append(", ");
+ appendAccess(access | ACCESS_CLASS);
+ buf.append(", ");
+ appendConstant(name);
+ buf.append(", ");
+ appendConstant(signature);
+ buf.append(", ");
+ appendConstant(superName);
+ buf.append(", ");
+ if (interfaces != null && interfaces.length > 0) {
+ buf.append("new String[] {");
+ for (int i = 0; i < interfaces.length; ++i) {
+ buf.append(i == 0 ? " " : ", ");
+ appendConstant(interfaces[i]);
+ }
+ buf.append(" }");
+ } else {
+ buf.append("null");
+ }
+ buf.append(");\n\n");
+ text.add(buf.toString());
+ }
+
+ public void visitSource(final String file, final String debug) {
+ buf.setLength(0);
+ buf.append("cw.visitSource(");
+ appendConstant(file);
+ buf.append(", ");
+ appendConstant(debug);
+ buf.append(");\n\n");
+ text.add(buf.toString());
+ }
+
+ public void visitOuterClass(
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ buf.setLength(0);
+ buf.append("cw.visitOuterClass(");
+ appendConstant(owner);
+ buf.append(", ");
+ appendConstant(name);
+ buf.append(", ");
+ appendConstant(desc);
+ buf.append(");\n\n");
+ text.add(buf.toString());
+ }
+
+ public void visitInnerClass(
+ final String name,
+ final String outerName,
+ final String innerName,
+ final int access)
+ {
+ buf.setLength(0);
+ buf.append("cw.visitInnerClass(");
+ appendConstant(name);
+ buf.append(", ");
+ appendConstant(outerName);
+ buf.append(", ");
+ appendConstant(innerName);
+ buf.append(", ");
+ appendAccess(access | ACCESS_INNER);
+ buf.append(");\n\n");
+ text.add(buf.toString());
+ }
+
+ public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value)
+ {
+ buf.setLength(0);
+ buf.append("{\n");
+ buf.append("fv = cw.visitField(");
+ appendAccess(access | ACCESS_FIELD);
+ buf.append(", ");
+ appendConstant(name);
+ buf.append(", ");
+ appendConstant(desc);
+ buf.append(", ");
+ appendConstant(signature);
+ buf.append(", ");
+ appendConstant(value);
+ buf.append(");\n");
+ text.add(buf.toString());
+ ASMifierFieldVisitor aav = new ASMifierFieldVisitor();
+ text.add(aav.getText());
+ text.add("}\n");
+ return aav;
+ }
+
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions)
+ {
+ buf.setLength(0);
+ buf.append("{\n");
+ buf.append("mv = cw.visitMethod(");
+ appendAccess(access);
+ buf.append(", ");
+ appendConstant(name);
+ buf.append(", ");
+ appendConstant(desc);
+ buf.append(", ");
+ appendConstant(signature);
+ buf.append(", ");
+ if (exceptions != null && exceptions.length > 0) {
+ buf.append("new String[] {");
+ for (int i = 0; i < exceptions.length; ++i) {
+ buf.append(i == 0 ? " " : ", ");
+ appendConstant(exceptions[i]);
+ }
+ buf.append(" }");
+ } else {
+ buf.append("null");
+ }
+ buf.append(");\n");
+ text.add(buf.toString());
+ ASMifierMethodVisitor acv = createASMifierMethodVisitor();
+ text.add(acv.getText());
+ text.add("}\n");
+ return acv;
+ }
+
+ protected ASMifierMethodVisitor createASMifierMethodVisitor() {
+ return new ASMifierMethodVisitor();
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ buf.setLength(0);
+ buf.append("{\n");
+ buf.append("av0 = cw.visitAnnotation(");
+ appendConstant(desc);
+ buf.append(", ");
+ buf.append(visible);
+ buf.append(");\n");
+ text.add(buf.toString());
+ ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
+ text.add(av.getText());
+ text.add("}\n");
+ return av;
+ }
+
+ public void visitEnd() {
+ text.add("cw.visitEnd();\n\n");
+ text.add("return cw.toByteArray();\n");
+ text.add("}\n");
+ text.add("}\n");
+ printList(pw, text);
+ pw.flush();
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Appends a string representation of the given access modifiers to {@link
+ * #buf buf}.
+ *
+ * @param access some access modifiers.
+ */
+ void appendAccess(final int access) {
+ boolean first = true;
+ if ((access & Opcodes.ACC_PUBLIC) != 0) {
+ buf.append("ACC_PUBLIC");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_PRIVATE) != 0) {
+ buf.append("ACC_PRIVATE");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_PROTECTED) != 0) {
+ buf.append("ACC_PROTECTED");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_FINAL) != 0) {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_FINAL");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_STATIC) != 0) {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_STATIC");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
+ if (!first) {
+ buf.append(" + ");
+ }
+ if ((access & ACCESS_CLASS) == 0) {
+ buf.append("ACC_SYNCHRONIZED");
+ } else {
+ buf.append("ACC_SUPER");
+ }
+ first = false;
+ }
+ if ((access & Opcodes.ACC_VOLATILE) != 0
+ && (access & ACCESS_FIELD) != 0)
+ {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_VOLATILE");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_BRIDGE) != 0 && (access & ACCESS_CLASS) == 0
+ && (access & ACCESS_FIELD) == 0)
+ {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_BRIDGE");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_VARARGS) != 0 && (access & ACCESS_CLASS) == 0
+ && (access & ACCESS_FIELD) == 0)
+ {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_VARARGS");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_TRANSIENT) != 0
+ && (access & ACCESS_FIELD) != 0)
+ {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_TRANSIENT");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_NATIVE) != 0 && (access & ACCESS_CLASS) == 0
+ && (access & ACCESS_FIELD) == 0)
+ {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_NATIVE");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_ENUM) != 0
+ && ((access & ACCESS_CLASS) != 0
+ || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0))
+ {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_ENUM");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_ANNOTATION) != 0
+ && (access & ACCESS_CLASS) != 0)
+ {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_ANNOTATION");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_ABSTRACT) != 0) {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_ABSTRACT");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_INTERFACE) != 0) {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_INTERFACE");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_STRICT) != 0) {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_STRICT");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_SYNTHETIC");
+ first = false;
+ }
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ if (!first) {
+ buf.append(" + ");
+ }
+ buf.append("ACC_DEPRECATED");
+ first = false;
+ }
+ if (first) {
+ buf.append('0');
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/ASMifierFieldVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/ASMifierFieldVisitor.java
new file mode 100644
index 0000000..213d711
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/ASMifierFieldVisitor.java
@@ -0,0 +1,50 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.FieldVisitor;
+
+/**
+ * A {@link FieldVisitor} that prints the ASM code that generates the fields it
+ * visits.
+ *
+ * @author Eric Bruneton
+ */
+public class ASMifierFieldVisitor extends ASMifierAbstractVisitor implements
+ FieldVisitor
+{
+
+ /**
+ * Constructs a new {@link ASMifierFieldVisitor}.
+ */
+ public ASMifierFieldVisitor() {
+ super("fv");
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/ASMifierMethodVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/ASMifierMethodVisitor.java
new file mode 100644
index 0000000..58d9107
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/ASMifierMethodVisitor.java
@@ -0,0 +1,443 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Label;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+
+import java.util.HashMap;
+
+/**
+ * A {@link MethodVisitor} that prints the ASM code that generates the methods
+ * it visits.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class ASMifierMethodVisitor extends ASMifierAbstractVisitor implements
+ MethodVisitor
+{
+
+ /**
+ * Constructs a new {@link ASMifierMethodVisitor} object.
+ */
+ public ASMifierMethodVisitor() {
+ super("mv");
+ this.labelNames = new HashMap();
+ }
+
+ public AnnotationVisitor visitAnnotationDefault() {
+ buf.setLength(0);
+ buf.append("{\n").append("av0 = mv.visitAnnotationDefault();\n");
+ text.add(buf.toString());
+ ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
+ text.add(av.getText());
+ text.add("}\n");
+ return av;
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(
+ final int parameter,
+ final String desc,
+ final boolean visible)
+ {
+ buf.setLength(0);
+ buf.append("{\n")
+ .append("av0 = mv.visitParameterAnnotation(")
+ .append(parameter)
+ .append(", ");
+ appendConstant(desc);
+ buf.append(", ").append(visible).append(");\n");
+ text.add(buf.toString());
+ ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
+ text.add(av.getText());
+ text.add("}\n");
+ return av;
+ }
+
+ public void visitCode() {
+ text.add("mv.visitCode();\n");
+ }
+
+ public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack)
+ {
+ buf.setLength(0);
+ switch (type) {
+ case Opcodes.F_NEW:
+ case Opcodes.F_FULL:
+ declareFrameTypes(nLocal, local);
+ declareFrameTypes(nStack, stack);
+ if (type == Opcodes.F_NEW) {
+ buf.append("mv.visitFrame(Opcodes.F_NEW, ");
+ } else {
+ buf.append("mv.visitFrame(Opcodes.F_FULL, ");
+ }
+ buf.append(nLocal).append(", new Object[] {");
+ appendFrameTypes(nLocal, local);
+ buf.append("}, ").append(nStack).append(", new Object[] {");
+ appendFrameTypes(nStack, stack);
+ buf.append('}');
+ break;
+ case Opcodes.F_APPEND:
+ declareFrameTypes(nLocal, local);
+ buf.append("mv.visitFrame(Opcodes.F_APPEND,")
+ .append(nLocal)
+ .append(", new Object[] {");
+ appendFrameTypes(nLocal, local);
+ buf.append("}, 0, null");
+ break;
+ case Opcodes.F_CHOP:
+ buf.append("mv.visitFrame(Opcodes.F_CHOP,")
+ .append(nLocal)
+ .append(", null, 0, null");
+ break;
+ case Opcodes.F_SAME:
+ buf.append("mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null");
+ break;
+ case Opcodes.F_SAME1:
+ declareFrameTypes(1, stack);
+ buf.append("mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {");
+ appendFrameTypes(1, stack);
+ buf.append('}');
+ break;
+ }
+ buf.append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitInsn(final int opcode) {
+ buf.setLength(0);
+ buf.append("mv.visitInsn(").append(OPCODES[opcode]).append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitIntInsn(final int opcode, final int operand) {
+ buf.setLength(0);
+ buf.append("mv.visitIntInsn(")
+ .append(OPCODES[opcode])
+ .append(", ")
+ .append(opcode == Opcodes.NEWARRAY
+ ? TYPES[operand]
+ : Integer.toString(operand))
+ .append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitVarInsn(final int opcode, final int var) {
+ buf.setLength(0);
+ buf.append("mv.visitVarInsn(")
+ .append(OPCODES[opcode])
+ .append(", ")
+ .append(var)
+ .append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitTypeInsn(final int opcode, final String type) {
+ buf.setLength(0);
+ buf.append("mv.visitTypeInsn(").append(OPCODES[opcode]).append(", ");
+ appendConstant(type);
+ buf.append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ buf.setLength(0);
+ buf.append("mv.visitFieldInsn(").append(OPCODES[opcode]).append(", ");
+ appendConstant(owner);
+ buf.append(", ");
+ appendConstant(name);
+ buf.append(", ");
+ appendConstant(desc);
+ buf.append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ buf.setLength(0);
+ buf.append("mv.visitMethodInsn(").append(OPCODES[opcode]).append(", ");
+ appendConstant(owner);
+ buf.append(", ");
+ appendConstant(name);
+ buf.append(", ");
+ appendConstant(desc);
+ buf.append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitJumpInsn(final int opcode, final Label label) {
+ buf.setLength(0);
+ declareLabel(label);
+ buf.append("mv.visitJumpInsn(").append(OPCODES[opcode]).append(", ");
+ appendLabel(label);
+ buf.append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitLabel(final Label label) {
+ buf.setLength(0);
+ declareLabel(label);
+ buf.append("mv.visitLabel(");
+ appendLabel(label);
+ buf.append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitLdcInsn(final Object cst) {
+ buf.setLength(0);
+ buf.append("mv.visitLdcInsn(");
+ appendConstant(cst);
+ buf.append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitIincInsn(final int var, final int increment) {
+ buf.setLength(0);
+ buf.append("mv.visitIincInsn(")
+ .append(var)
+ .append(", ")
+ .append(increment)
+ .append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label[] labels)
+ {
+ buf.setLength(0);
+ for (int i = 0; i < labels.length; ++i) {
+ declareLabel(labels[i]);
+ }
+ declareLabel(dflt);
+
+ buf.append("mv.visitTableSwitchInsn(")
+ .append(min)
+ .append(", ")
+ .append(max)
+ .append(", ");
+ appendLabel(dflt);
+ buf.append(", new Label[] {");
+ for (int i = 0; i < labels.length; ++i) {
+ buf.append(i == 0 ? " " : ", ");
+ appendLabel(labels[i]);
+ }
+ buf.append(" });\n");
+ text.add(buf.toString());
+ }
+
+ public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int[] keys,
+ final Label[] labels)
+ {
+ buf.setLength(0);
+ for (int i = 0; i < labels.length; ++i) {
+ declareLabel(labels[i]);
+ }
+ declareLabel(dflt);
+
+ buf.append("mv.visitLookupSwitchInsn(");
+ appendLabel(dflt);
+ buf.append(", new int[] {");
+ for (int i = 0; i < keys.length; ++i) {
+ buf.append(i == 0 ? " " : ", ").append(keys[i]);
+ }
+ buf.append(" }, new Label[] {");
+ for (int i = 0; i < labels.length; ++i) {
+ buf.append(i == 0 ? " " : ", ");
+ appendLabel(labels[i]);
+ }
+ buf.append(" });\n");
+ text.add(buf.toString());
+ }
+
+ public void visitMultiANewArrayInsn(final String desc, final int dims) {
+ buf.setLength(0);
+ buf.append("mv.visitMultiANewArrayInsn(");
+ appendConstant(desc);
+ buf.append(", ").append(dims).append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitTryCatchBlock(
+ final Label start,
+ final Label end,
+ final Label handler,
+ final String type)
+ {
+ buf.setLength(0);
+ declareLabel(start);
+ declareLabel(end);
+ declareLabel(handler);
+ buf.append("mv.visitTryCatchBlock(");
+ appendLabel(start);
+ buf.append(", ");
+ appendLabel(end);
+ buf.append(", ");
+ appendLabel(handler);
+ buf.append(", ");
+ appendConstant(type);
+ buf.append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index)
+ {
+ buf.setLength(0);
+ buf.append("mv.visitLocalVariable(");
+ appendConstant(name);
+ buf.append(", ");
+ appendConstant(desc);
+ buf.append(", ");
+ appendConstant(signature);
+ buf.append(", ");
+ appendLabel(start);
+ buf.append(", ");
+ appendLabel(end);
+ buf.append(", ").append(index).append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitLineNumber(final int line, final Label start) {
+ buf.setLength(0);
+ buf.append("mv.visitLineNumber(").append(line).append(", ");
+ appendLabel(start);
+ buf.append(");\n");
+ text.add(buf.toString());
+ }
+
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ buf.setLength(0);
+ buf.append("mv.visitMaxs(")
+ .append(maxStack)
+ .append(", ")
+ .append(maxLocals)
+ .append(");\n");
+ text.add(buf.toString());
+ }
+
+ private void declareFrameTypes(final int n, final Object[] o) {
+ for (int i = 0; i < n; ++i) {
+ if (o[i] instanceof Label) {
+ declareLabel((Label) o[i]);
+ }
+ }
+ }
+
+ private void appendFrameTypes(final int n, final Object[] o) {
+ for (int i = 0; i < n; ++i) {
+ if (i > 0) {
+ buf.append(", ");
+ }
+ if (o[i] instanceof String) {
+ appendConstant(o[i]);
+ } else if (o[i] instanceof Integer) {
+ switch (((Integer) o[i]).intValue()) {
+ case 0:
+ buf.append("Opcodes.TOP");
+ break;
+ case 1:
+ buf.append("Opcodes.INTEGER");
+ break;
+ case 2:
+ buf.append("Opcodes.FLOAT");
+ break;
+ case 3:
+ buf.append("Opcodes.DOUBLE");
+ break;
+ case 4:
+ buf.append("Opcodes.LONG");
+ break;
+ case 5:
+ buf.append("Opcodes.NULL");
+ break;
+ case 6:
+ buf.append("Opcodes.UNINITIALIZED_THIS");
+ break;
+ }
+ } else {
+ appendLabel((Label) o[i]);
+ }
+ }
+ }
+
+ /**
+ * Appends a declaration of the given label to {@link #buf buf}. This
+ * declaration is of the form "Label lXXX = new Label();". Does nothing if
+ * the given label has already been declared.
+ *
+ * @param l a label.
+ */
+ private void declareLabel(final Label l) {
+ String name = (String) labelNames.get(l);
+ if (name == null) {
+ name = "l" + labelNames.size();
+ labelNames.put(l, name);
+ buf.append("Label ").append(name).append(" = new Label();\n");
+ }
+ }
+
+ /**
+ * Appends the name of the given label to {@link #buf buf}. The given label
+ * <i>must</i> already have a name. One way to ensure this is to always
+ * call {@link #declareLabel declared} before calling this method.
+ *
+ * @param l a label.
+ */
+ private void appendLabel(final Label l) {
+ buf.append((String) labelNames.get(l));
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/AbstractVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/AbstractVisitor.java
new file mode 100644
index 0000000..c369151
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/AbstractVisitor.java
@@ -0,0 +1,202 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.mockito.asm.Attribute;
+
+/**
+ * An abstract visitor.
+ *
+ * @author Eric Bruneton
+ */
+public abstract class AbstractVisitor {
+
+ /**
+ * The names of the Java Virtual Machine opcodes.
+ */
+ public static final String[] OPCODES;
+ /**
+ * Types for <code>operand</code> parameter of the
+ * {@link org.mockito.asm.MethodVisitor#visitIntInsn} method when
+ * <code>opcode</code> is <code>NEWARRAY</code>.
+ */
+ public static final String[] TYPES;
+
+ static {
+ String s = "NOP,ACONST_NULL,ICONST_M1,ICONST_0,ICONST_1,ICONST_2,"
+ + "ICONST_3,ICONST_4,ICONST_5,LCONST_0,LCONST_1,FCONST_0,"
+ + "FCONST_1,FCONST_2,DCONST_0,DCONST_1,BIPUSH,SIPUSH,LDC,,,"
+ + "ILOAD,LLOAD,FLOAD,DLOAD,ALOAD,,,,,,,,,,,,,,,,,,,,,IALOAD,"
+ + "LALOAD,FALOAD,DALOAD,AALOAD,BALOAD,CALOAD,SALOAD,ISTORE,"
+ + "LSTORE,FSTORE,DSTORE,ASTORE,,,,,,,,,,,,,,,,,,,,,IASTORE,"
+ + "LASTORE,FASTORE,DASTORE,AASTORE,BASTORE,CASTORE,SASTORE,POP,"
+ + "POP2,DUP,DUP_X1,DUP_X2,DUP2,DUP2_X1,DUP2_X2,SWAP,IADD,LADD,"
+ + "FADD,DADD,ISUB,LSUB,FSUB,DSUB,IMUL,LMUL,FMUL,DMUL,IDIV,LDIV,"
+ + "FDIV,DDIV,IREM,LREM,FREM,DREM,INEG,LNEG,FNEG,DNEG,ISHL,LSHL,"
+ + "ISHR,LSHR,IUSHR,LUSHR,IAND,LAND,IOR,LOR,IXOR,LXOR,IINC,I2L,"
+ + "I2F,I2D,L2I,L2F,L2D,F2I,F2L,F2D,D2I,D2L,D2F,I2B,I2C,I2S,LCMP,"
+ + "FCMPL,FCMPG,DCMPL,DCMPG,IFEQ,IFNE,IFLT,IFGE,IFGT,IFLE,"
+ + "IF_ICMPEQ,IF_ICMPNE,IF_ICMPLT,IF_ICMPGE,IF_ICMPGT,IF_ICMPLE,"
+ + "IF_ACMPEQ,IF_ACMPNE,GOTO,JSR,RET,TABLESWITCH,LOOKUPSWITCH,"
+ + "IRETURN,LRETURN,FRETURN,DRETURN,ARETURN,RETURN,GETSTATIC,"
+ + "PUTSTATIC,GETFIELD,PUTFIELD,INVOKEVIRTUAL,INVOKESPECIAL,"
+ + "INVOKESTATIC,INVOKEINTERFACE,,NEW,NEWARRAY,ANEWARRAY,"
+ + "ARRAYLENGTH,ATHROW,CHECKCAST,INSTANCEOF,MONITORENTER,"
+ + "MONITOREXIT,,MULTIANEWARRAY,IFNULL,IFNONNULL,";
+ OPCODES = new String[200];
+ int i = 0;
+ int j = 0;
+ int l;
+ while ((l = s.indexOf(',', j)) > 0) {
+ OPCODES[i++] = j + 1 == l ? null : s.substring(j, l);
+ j = l + 1;
+ }
+
+ s = "T_BOOLEAN,T_CHAR,T_FLOAT,T_DOUBLE,T_BYTE,T_SHORT,T_INT,T_LONG,";
+ TYPES = new String[12];
+ j = 0;
+ i = 4;
+ while ((l = s.indexOf(',', j)) > 0) {
+ TYPES[i++] = s.substring(j, l);
+ j = l + 1;
+ }
+ }
+
+ /**
+ * The text to be printed. Since the code of methods is not necessarily
+ * visited in sequential order, one method after the other, but can be
+ * interlaced (some instructions from method one, then some instructions
+ * from method two, then some instructions from method one again...), it is
+ * not possible to print the visited instructions directly to a sequential
+ * stream. A class is therefore printed in a two steps process: a string
+ * tree is constructed during the visit, and printed to a sequential stream
+ * at the end of the visit. This string tree is stored in this field, as a
+ * string list that can contain other string lists, which can themselves
+ * contain other string lists, and so on.
+ */
+ public final List text;
+
+ /**
+ * A buffer that can be used to create strings.
+ */
+ protected final StringBuffer buf;
+
+ /**
+ * Constructs a new {@link AbstractVisitor}.
+ */
+ protected AbstractVisitor() {
+ this.text = new ArrayList();
+ this.buf = new StringBuffer();
+ }
+
+ /**
+ * Returns the text constructed by this visitor.
+ *
+ * @return the text constructed by this visitor.
+ */
+ public List getText() {
+ return text;
+ }
+
+ /**
+ * Prints the text constructed by this visitor.
+ *
+ * @param pw the print writer to be used.
+ */
+ public void print(final PrintWriter pw) {
+ printList(pw, text);
+ }
+
+ /**
+ * Appends a quoted string to a given buffer.
+ *
+ * @param buf the buffer where the string must be added.
+ * @param s the string to be added.
+ */
+ public static void appendString(final StringBuffer buf, final String s) {
+ buf.append('\"');
+ for (int i = 0; i < s.length(); ++i) {
+ char c = s.charAt(i);
+ if (c == '\n') {
+ buf.append("\\n");
+ } else if (c == '\r') {
+ buf.append("\\r");
+ } else if (c == '\\') {
+ buf.append("\\\\");
+ } else if (c == '"') {
+ buf.append("\\\"");
+ } else if (c < 0x20 || c > 0x7f) {
+ buf.append("\\u");
+ if (c < 0x10) {
+ buf.append("000");
+ } else if (c < 0x100) {
+ buf.append("00");
+ } else if (c < 0x1000) {
+ buf.append('0');
+ }
+ buf.append(Integer.toString(c, 16));
+ } else {
+ buf.append(c);
+ }
+ }
+ buf.append('\"');
+ }
+
+ /**
+ * Prints the given string tree.
+ *
+ * @param pw the writer to be used to print the tree.
+ * @param l a string tree, i.e., a string list that can contain other string
+ * lists, and so on recursively.
+ */
+ static void printList(final PrintWriter pw, final List l) {
+ for (int i = 0; i < l.size(); ++i) {
+ Object o = l.get(i);
+ if (o instanceof List) {
+ printList(pw, (List) o);
+ } else {
+ pw.print(o.toString());
+ }
+ }
+ }
+
+ /**
+ * Returns the default {@link ASMifiable} prototypes.
+ *
+ * @return the default {@link ASMifiable} prototypes.
+ */
+ public static Attribute[] getDefaultAttributes() {
+ return new Attribute[0];
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/CheckAnnotationAdapter.java b/cglib-and-asm/src/org/mockito/asm/util/CheckAnnotationAdapter.java
new file mode 100644
index 0000000..8afa075
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/CheckAnnotationAdapter.java
@@ -0,0 +1,132 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Type;
+
+/**
+ * An {@link AnnotationVisitor} that checks that its methods are properly used.
+ *
+ * @author Eric Bruneton
+ */
+public class CheckAnnotationAdapter implements AnnotationVisitor {
+
+ private final AnnotationVisitor av;
+
+ private final boolean named;
+
+ private boolean end;
+
+ public CheckAnnotationAdapter(final AnnotationVisitor av) {
+ this(av, true);
+ }
+
+ CheckAnnotationAdapter(final AnnotationVisitor av, final boolean named) {
+ this.av = av;
+ this.named = named;
+ }
+
+ public void visit(final String name, final Object value) {
+ checkEnd();
+ checkName(name);
+ if (!(value instanceof Byte || value instanceof Boolean
+ || value instanceof Character || value instanceof Short
+ || value instanceof Integer || value instanceof Long
+ || value instanceof Float || value instanceof Double
+ || value instanceof String || value instanceof Type
+ || value instanceof byte[] || value instanceof boolean[]
+ || value instanceof char[] || value instanceof short[]
+ || value instanceof int[] || value instanceof long[]
+ || value instanceof float[] || value instanceof double[]))
+ {
+ throw new IllegalArgumentException("Invalid annotation value");
+ }
+ if (av != null) {
+ av.visit(name, value);
+ }
+ }
+
+ public void visitEnum(
+ final String name,
+ final String desc,
+ final String value)
+ {
+ checkEnd();
+ checkName(name);
+ CheckMethodAdapter.checkDesc(desc, false);
+ if (value == null) {
+ throw new IllegalArgumentException("Invalid enum value");
+ }
+ if (av != null) {
+ av.visitEnum(name, desc, value);
+ }
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String name,
+ final String desc)
+ {
+ checkEnd();
+ checkName(name);
+ CheckMethodAdapter.checkDesc(desc, false);
+ return new CheckAnnotationAdapter(av == null
+ ? null
+ : av.visitAnnotation(name, desc));
+ }
+
+ public AnnotationVisitor visitArray(final String name) {
+ checkEnd();
+ checkName(name);
+ return new CheckAnnotationAdapter(av == null
+ ? null
+ : av.visitArray(name), false);
+ }
+
+ public void visitEnd() {
+ checkEnd();
+ end = true;
+ if (av != null) {
+ av.visitEnd();
+ }
+ }
+
+ private void checkEnd() {
+ if (end) {
+ throw new IllegalStateException("Cannot call a visit method after visitEnd has been called");
+ }
+ }
+
+ private void checkName(final String name) {
+ if (named && name == null) {
+ throw new IllegalArgumentException("Annotation value name must not be null");
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/CheckClassAdapter.java b/cglib-and-asm/src/org/mockito/asm/util/CheckClassAdapter.java
new file mode 100644
index 0000000..d828efd
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/CheckClassAdapter.java
@@ -0,0 +1,480 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import java.io.FileInputStream;
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Attribute;
+import org.mockito.asm.ClassAdapter;
+import org.mockito.asm.ClassReader;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.FieldVisitor;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+import org.mockito.asm.Type;
+import org.mockito.asm.tree.ClassNode;
+import org.mockito.asm.tree.MethodNode;
+import org.mockito.asm.tree.TryCatchBlockNode;
+import org.mockito.asm.tree.analysis.Analyzer;
+import org.mockito.asm.tree.analysis.Frame;
+import org.mockito.asm.tree.analysis.SimpleVerifier;
+
+/**
+ * A {@link ClassAdapter} that checks that its methods are properly used. More
+ * precisely this class adapter checks each method call individually, based
+ * <i>only</i> on its arguments, but does <i>not</i> check the <i>sequence</i>
+ * of method calls. For example, the invalid sequence
+ * <tt>visitField(ACC_PUBLIC, "i", "I", null)</tt> <tt>visitField(ACC_PUBLIC,
+ * "i", "D", null)</tt>
+ * will <i>not</i> be detected by this class adapter.
+ *
+ * <p><code>CheckClassAdapter</code> can be also used to verify bytecode
+ * transformations in order to make sure transformed bytecode is sane. For
+ * example:
+ *
+ * <pre>
+ * InputStream is = ...; // get bytes for the source class
+ * ClassReader cr = new ClassReader(is);
+ * ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
+ * ClassVisitor cv = new <b>MyClassAdapter</b>(new CheckClassAdapter(cw));
+ * cr.accept(cv, 0);
+ *
+ * StringWriter sw = new StringWriter();
+ * PrintWriter pw = new PrintWriter(sw);
+ * CheckClassAdapter.verify(new ClassReader(cw.toByteArray()), false, pw);
+ * assertTrue(sw.toString(), sw.toString().length()==0);
+ * </pre>
+ *
+ * Above code runs transformed bytecode trough the
+ * <code>CheckClassAdapter</code>. It won't be exactly the same verification
+ * as JVM does, but it run data flow analysis for the code of each method and
+ * checks that expectations are met for each method instruction.
+ *
+ * <p>If method bytecode has errors, assertion text will show the erroneous
+ * instruction number and dump of the failed method with information about
+ * locals and stack slot for each instruction. For example (format is -
+ * insnNumber locals : stack):
+ *
+ * <pre>
+ * org.mockito.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found .
+ * at org.mockito.asm.tree.analysis.Analyzer.analyze(Analyzer.java:289)
+ * at org.mockito.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:135)
+ * ...
+ * remove()V
+ * 00000 LinkedBlockingQueue$Itr . . . . . . . . :
+ * ICONST_0
+ * 00001 LinkedBlockingQueue$Itr . . . . . . . . : I
+ * ISTORE 2
+ * 00001 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . :
+ * ...
+ *
+ * 00071 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . :
+ * ILOAD 1
+ * 00072 <b>?</b>
+ * INVOKESPECIAL java/lang/Integer.<init> (I)V
+ * ...
+ * </pre>
+ *
+ * In the above output you can see that variable 1 loaded by
+ * <code>ILOAD 1</code> instruction at position <code>00071</code> is not
+ * initialized. You can also see that at the beginning of the method (code
+ * inserted by the transformation) variable 2 is initialized.
+ *
+ * <p>Note that when used like that, <code>CheckClassAdapter.verify()</code>
+ * can trigger additional class loading, because it is using
+ * <code>SimpleVerifier</code>.
+ *
+ * @author Eric Bruneton
+ */
+public class CheckClassAdapter extends ClassAdapter {
+
+ /**
+ * <tt>true</tt> if the visit method has been called.
+ */
+ private boolean start;
+
+ /**
+ * <tt>true</tt> if the visitSource method has been called.
+ */
+ private boolean source;
+
+ /**
+ * <tt>true</tt> if the visitOuterClass method has been called.
+ */
+ private boolean outer;
+
+ /**
+ * <tt>true</tt> if the visitEnd method has been called.
+ */
+ private boolean end;
+
+ /**
+ * Checks a given class. <p> Usage: CheckClassAdapter &lt;fully qualified
+ * class name or class file name&gt;
+ *
+ * @param args the command line arguments.
+ *
+ * @throws Exception if the class cannot be found, or if an IO exception
+ * occurs.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 1) {
+ System.err.println("Verifies the given class.");
+ System.err.println("Usage: CheckClassAdapter "
+ + "<fully qualified class name or class file name>");
+ return;
+ }
+ ClassReader cr;
+ if (args[0].endsWith(".class")) {
+ cr = new ClassReader(new FileInputStream(args[0]));
+ } else {
+ cr = new ClassReader(args[0]);
+ }
+
+ verify(cr, false, new PrintWriter(System.err));
+ }
+
+ /**
+ * Checks a given class
+ *
+ * @param cr a <code>ClassReader</code> that contains bytecode for the
+ * analysis.
+ * @param dump true if bytecode should be printed out not only when errors
+ * are found.
+ * @param pw write where results going to be printed
+ */
+ public static void verify(
+ final ClassReader cr,
+ final boolean dump,
+ final PrintWriter pw)
+ {
+ ClassNode cn = new ClassNode();
+ cr.accept(new CheckClassAdapter(cn), ClassReader.SKIP_DEBUG);
+
+ Type syperType = cn.superName == null
+ ? null
+ : Type.getObjectType(cn.superName);
+ List methods = cn.methods;
+ for (int i = 0; i < methods.size(); ++i) {
+ MethodNode method = (MethodNode) methods.get(i);
+ Analyzer a = new Analyzer(new SimpleVerifier(Type.getObjectType(cn.name),
+ syperType,
+ false));
+ try {
+ a.analyze(cn.name, method);
+ if (!dump) {
+ continue;
+ }
+ } catch (Exception e) {
+ e.printStackTrace(pw);
+ }
+ Frame[] frames = a.getFrames();
+
+ TraceMethodVisitor mv = new TraceMethodVisitor();
+
+ pw.println(method.name + method.desc);
+ for (int j = 0; j < method.instructions.size(); ++j) {
+ method.instructions.get(j).accept(mv);
+
+ StringBuffer s = new StringBuffer();
+ Frame f = frames[j];
+ if (f == null) {
+ s.append('?');
+ } else {
+ for (int k = 0; k < f.getLocals(); ++k) {
+ s.append(getShortName(f.getLocal(k).toString()))
+ .append(' ');
+ }
+ s.append(" : ");
+ for (int k = 0; k < f.getStackSize(); ++k) {
+ s.append(getShortName(f.getStack(k).toString()))
+ .append(' ');
+ }
+ }
+ while (s.length() < method.maxStack + method.maxLocals + 1) {
+ s.append(' ');
+ }
+ pw.print(Integer.toString(j + 100000).substring(1));
+ pw.print(" " + s + " : " + mv.buf); // mv.text.get(j));
+ }
+ for (int j = 0; j < method.tryCatchBlocks.size(); ++j) {
+ ((TryCatchBlockNode) method.tryCatchBlocks.get(j)).accept(mv);
+ pw.print(" " + mv.buf);
+ }
+ pw.println();
+ }
+ pw.flush();
+ }
+
+ private static String getShortName(final String name) {
+ int n = name.lastIndexOf('/');
+ int k = name.length();
+ if (name.charAt(k - 1) == ';') {
+ k--;
+ }
+ return n == -1 ? name : name.substring(n + 1, k);
+ }
+
+ /**
+ * Constructs a new {@link CheckClassAdapter}.
+ *
+ * @param cv the class visitor to which this adapter must delegate calls.
+ */
+ public CheckClassAdapter(final ClassVisitor cv) {
+ super(cv);
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the ClassVisitor interface
+ // ------------------------------------------------------------------------
+
+ public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces)
+ {
+ if (start) {
+ throw new IllegalStateException("visit must be called only once");
+ }
+ start = true;
+ checkState();
+ checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL
+ + Opcodes.ACC_SUPER + Opcodes.ACC_INTERFACE
+ + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
+ + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM
+ + Opcodes.ACC_DEPRECATED);
+ if (name == null || !name.endsWith("package-info")) {
+ CheckMethodAdapter.checkInternalName(name, "class name");
+ }
+ if ("java/lang/Object".equals(name)) {
+ if (superName != null) {
+ throw new IllegalArgumentException("The super class name of the Object class must be 'null'");
+ }
+ } else {
+ CheckMethodAdapter.checkInternalName(superName, "super class name");
+ }
+ if (signature != null) {
+ CheckMethodAdapter.checkClassSignature(signature);
+ }
+ if ((access & Opcodes.ACC_INTERFACE) != 0) {
+ if (!"java/lang/Object".equals(superName)) {
+ throw new IllegalArgumentException("The super class name of interfaces must be 'java/lang/Object'");
+ }
+ }
+ if (interfaces != null) {
+ for (int i = 0; i < interfaces.length; ++i) {
+ CheckMethodAdapter.checkInternalName(interfaces[i],
+ "interface name at index " + i);
+ }
+ }
+ cv.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ public void visitSource(final String file, final String debug) {
+ checkState();
+ if (source) {
+ throw new IllegalStateException("visitSource can be called only once.");
+ }
+ source = true;
+ cv.visitSource(file, debug);
+ }
+
+ public void visitOuterClass(
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ checkState();
+ if (outer) {
+ throw new IllegalStateException("visitOuterClass can be called only once.");
+ }
+ outer = true;
+ if (owner == null) {
+ throw new IllegalArgumentException("Illegal outer class owner");
+ }
+ if (desc != null) {
+ CheckMethodAdapter.checkMethodDesc(desc);
+ }
+ cv.visitOuterClass(owner, name, desc);
+ }
+
+ public void visitInnerClass(
+ final String name,
+ final String outerName,
+ final String innerName,
+ final int access)
+ {
+ checkState();
+ CheckMethodAdapter.checkInternalName(name, "class name");
+ if (outerName != null) {
+ CheckMethodAdapter.checkInternalName(outerName, "outer class name");
+ }
+ if (innerName != null) {
+ CheckMethodAdapter.checkIdentifier(innerName, "inner class name");
+ }
+ checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
+ + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
+ + Opcodes.ACC_FINAL + Opcodes.ACC_INTERFACE
+ + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
+ + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM);
+ cv.visitInnerClass(name, outerName, innerName, access);
+ }
+
+ public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value)
+ {
+ checkState();
+ checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
+ + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
+ + Opcodes.ACC_FINAL + Opcodes.ACC_VOLATILE
+ + Opcodes.ACC_TRANSIENT + Opcodes.ACC_SYNTHETIC
+ + Opcodes.ACC_ENUM + Opcodes.ACC_DEPRECATED);
+ CheckMethodAdapter.checkIdentifier(name, "field name");
+ CheckMethodAdapter.checkDesc(desc, false);
+ if (signature != null) {
+ CheckMethodAdapter.checkFieldSignature(signature);
+ }
+ if (value != null) {
+ CheckMethodAdapter.checkConstant(value);
+ }
+ FieldVisitor av = cv.visitField(access, name, desc, signature, value);
+ return new CheckFieldAdapter(av);
+ }
+
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions)
+ {
+ checkState();
+ checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
+ + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
+ + Opcodes.ACC_FINAL + Opcodes.ACC_SYNCHRONIZED
+ + Opcodes.ACC_BRIDGE + Opcodes.ACC_VARARGS + Opcodes.ACC_NATIVE
+ + Opcodes.ACC_ABSTRACT + Opcodes.ACC_STRICT
+ + Opcodes.ACC_SYNTHETIC + Opcodes.ACC_DEPRECATED);
+ CheckMethodAdapter.checkMethodIdentifier(name, "method name");
+ CheckMethodAdapter.checkMethodDesc(desc);
+ if (signature != null) {
+ CheckMethodAdapter.checkMethodSignature(signature);
+ }
+ if (exceptions != null) {
+ for (int i = 0; i < exceptions.length; ++i) {
+ CheckMethodAdapter.checkInternalName(exceptions[i],
+ "exception name at index " + i);
+ }
+ }
+ return new CheckMethodAdapter(cv.visitMethod(access,
+ name,
+ desc,
+ signature,
+ exceptions));
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ checkState();
+ CheckMethodAdapter.checkDesc(desc, false);
+ return new CheckAnnotationAdapter(cv.visitAnnotation(desc, visible));
+ }
+
+ public void visitAttribute(final Attribute attr) {
+ checkState();
+ if (attr == null) {
+ throw new IllegalArgumentException("Invalid attribute (must not be null)");
+ }
+ cv.visitAttribute(attr);
+ }
+
+ public void visitEnd() {
+ checkState();
+ end = true;
+ cv.visitEnd();
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Checks that the visit method has been called and that visitEnd has not
+ * been called.
+ */
+ private void checkState() {
+ if (!start) {
+ throw new IllegalStateException("Cannot visit member before visit has been called.");
+ }
+ if (end) {
+ throw new IllegalStateException("Cannot visit member after visitEnd has been called.");
+ }
+ }
+
+ /**
+ * Checks that the given access flags do not contain invalid flags. This
+ * method also checks that mutually incompatible flags are not set
+ * simultaneously.
+ *
+ * @param access the access flags to be checked
+ * @param possibleAccess the valid access flags.
+ */
+ static void checkAccess(final int access, final int possibleAccess) {
+ if ((access & ~possibleAccess) != 0) {
+ throw new IllegalArgumentException("Invalid access flags: "
+ + access);
+ }
+ int pub = (access & Opcodes.ACC_PUBLIC) == 0 ? 0 : 1;
+ int pri = (access & Opcodes.ACC_PRIVATE) == 0 ? 0 : 1;
+ int pro = (access & Opcodes.ACC_PROTECTED) == 0 ? 0 : 1;
+ if (pub + pri + pro > 1) {
+ throw new IllegalArgumentException("public private and protected are mutually exclusive: "
+ + access);
+ }
+ int fin = (access & Opcodes.ACC_FINAL) == 0 ? 0 : 1;
+ int abs = (access & Opcodes.ACC_ABSTRACT) == 0 ? 0 : 1;
+ if (fin + abs > 1) {
+ throw new IllegalArgumentException("final and abstract are mutually exclusive: "
+ + access);
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/CheckFieldAdapter.java b/cglib-and-asm/src/org/mockito/asm/util/CheckFieldAdapter.java
new file mode 100644
index 0000000..82cc37d
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/CheckFieldAdapter.java
@@ -0,0 +1,77 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Attribute;
+import org.mockito.asm.FieldVisitor;
+
+/**
+ * A {@link FieldVisitor} that checks that its methods are properly used.
+ */
+public class CheckFieldAdapter implements FieldVisitor {
+
+ private final FieldVisitor fv;
+
+ private boolean end;
+
+ public CheckFieldAdapter(final FieldVisitor fv) {
+ this.fv = fv;
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ checkEnd();
+ CheckMethodAdapter.checkDesc(desc, false);
+ return new CheckAnnotationAdapter(fv.visitAnnotation(desc, visible));
+ }
+
+ public void visitAttribute(final Attribute attr) {
+ checkEnd();
+ if (attr == null) {
+ throw new IllegalArgumentException("Invalid attribute (must not be null)");
+ }
+ fv.visitAttribute(attr);
+ }
+
+ public void visitEnd() {
+ checkEnd();
+ end = true;
+ fv.visitEnd();
+ }
+
+ private void checkEnd() {
+ if (end) {
+ throw new IllegalStateException("Cannot call a visit method after visitEnd has been called");
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/CheckMethodAdapter.java b/cglib-and-asm/src/org/mockito/asm/util/CheckMethodAdapter.java
new file mode 100644
index 0000000..f255892
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/CheckMethodAdapter.java
@@ -0,0 +1,1328 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Attribute;
+import org.mockito.asm.Label;
+import org.mockito.asm.MethodAdapter;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+import org.mockito.asm.Type;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link MethodAdapter} that checks that its methods are properly used. More
+ * precisely this code adapter checks each instruction individually (i.e., each
+ * visit method checks some preconditions based <i>only</i> on its arguments -
+ * such as the fact that the given opcode is correct for a given visit method),
+ * but does <i>not</i> check the <i>sequence</i> of instructions. For example,
+ * in a method whose signature is <tt>void m ()</tt>, the invalid instruction
+ * IRETURN, or the invalid sequence IADD L2I will <i>not</i> be detected by
+ * this code adapter.
+ *
+ * @author Eric Bruneton
+ */
+public class CheckMethodAdapter extends MethodAdapter {
+
+ /**
+ * <tt>true</tt> if the visitCode method has been called.
+ */
+ private boolean startCode;
+
+ /**
+ * <tt>true</tt> if the visitMaxs method has been called.
+ */
+ private boolean endCode;
+
+ /**
+ * <tt>true</tt> if the visitEnd method has been called.
+ */
+ private boolean endMethod;
+
+ /**
+ * The already visited labels. This map associate Integer values to Label
+ * keys.
+ */
+ private final Map labels;
+
+ /**
+ * Code of the visit method to be used for each opcode.
+ */
+ private static final int[] TYPE;
+
+ static {
+ String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD"
+ + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD"
+ + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA";
+ TYPE = new int[s.length()];
+ for (int i = 0; i < TYPE.length; ++i) {
+ TYPE[i] = s.charAt(i) - 'A' - 1;
+ }
+ }
+
+ // code to generate the above string
+ // public static void main (String[] args) {
+ // int[] TYPE = new int[] {
+ // 0, //NOP
+ // 0, //ACONST_NULL
+ // 0, //ICONST_M1
+ // 0, //ICONST_0
+ // 0, //ICONST_1
+ // 0, //ICONST_2
+ // 0, //ICONST_3
+ // 0, //ICONST_4
+ // 0, //ICONST_5
+ // 0, //LCONST_0
+ // 0, //LCONST_1
+ // 0, //FCONST_0
+ // 0, //FCONST_1
+ // 0, //FCONST_2
+ // 0, //DCONST_0
+ // 0, //DCONST_1
+ // 1, //BIPUSH
+ // 1, //SIPUSH
+ // 7, //LDC
+ // -1, //LDC_W
+ // -1, //LDC2_W
+ // 2, //ILOAD
+ // 2, //LLOAD
+ // 2, //FLOAD
+ // 2, //DLOAD
+ // 2, //ALOAD
+ // -1, //ILOAD_0
+ // -1, //ILOAD_1
+ // -1, //ILOAD_2
+ // -1, //ILOAD_3
+ // -1, //LLOAD_0
+ // -1, //LLOAD_1
+ // -1, //LLOAD_2
+ // -1, //LLOAD_3
+ // -1, //FLOAD_0
+ // -1, //FLOAD_1
+ // -1, //FLOAD_2
+ // -1, //FLOAD_3
+ // -1, //DLOAD_0
+ // -1, //DLOAD_1
+ // -1, //DLOAD_2
+ // -1, //DLOAD_3
+ // -1, //ALOAD_0
+ // -1, //ALOAD_1
+ // -1, //ALOAD_2
+ // -1, //ALOAD_3
+ // 0, //IALOAD
+ // 0, //LALOAD
+ // 0, //FALOAD
+ // 0, //DALOAD
+ // 0, //AALOAD
+ // 0, //BALOAD
+ // 0, //CALOAD
+ // 0, //SALOAD
+ // 2, //ISTORE
+ // 2, //LSTORE
+ // 2, //FSTORE
+ // 2, //DSTORE
+ // 2, //ASTORE
+ // -1, //ISTORE_0
+ // -1, //ISTORE_1
+ // -1, //ISTORE_2
+ // -1, //ISTORE_3
+ // -1, //LSTORE_0
+ // -1, //LSTORE_1
+ // -1, //LSTORE_2
+ // -1, //LSTORE_3
+ // -1, //FSTORE_0
+ // -1, //FSTORE_1
+ // -1, //FSTORE_2
+ // -1, //FSTORE_3
+ // -1, //DSTORE_0
+ // -1, //DSTORE_1
+ // -1, //DSTORE_2
+ // -1, //DSTORE_3
+ // -1, //ASTORE_0
+ // -1, //ASTORE_1
+ // -1, //ASTORE_2
+ // -1, //ASTORE_3
+ // 0, //IASTORE
+ // 0, //LASTORE
+ // 0, //FASTORE
+ // 0, //DASTORE
+ // 0, //AASTORE
+ // 0, //BASTORE
+ // 0, //CASTORE
+ // 0, //SASTORE
+ // 0, //POP
+ // 0, //POP2
+ // 0, //DUP
+ // 0, //DUP_X1
+ // 0, //DUP_X2
+ // 0, //DUP2
+ // 0, //DUP2_X1
+ // 0, //DUP2_X2
+ // 0, //SWAP
+ // 0, //IADD
+ // 0, //LADD
+ // 0, //FADD
+ // 0, //DADD
+ // 0, //ISUB
+ // 0, //LSUB
+ // 0, //FSUB
+ // 0, //DSUB
+ // 0, //IMUL
+ // 0, //LMUL
+ // 0, //FMUL
+ // 0, //DMUL
+ // 0, //IDIV
+ // 0, //LDIV
+ // 0, //FDIV
+ // 0, //DDIV
+ // 0, //IREM
+ // 0, //LREM
+ // 0, //FREM
+ // 0, //DREM
+ // 0, //INEG
+ // 0, //LNEG
+ // 0, //FNEG
+ // 0, //DNEG
+ // 0, //ISHL
+ // 0, //LSHL
+ // 0, //ISHR
+ // 0, //LSHR
+ // 0, //IUSHR
+ // 0, //LUSHR
+ // 0, //IAND
+ // 0, //LAND
+ // 0, //IOR
+ // 0, //LOR
+ // 0, //IXOR
+ // 0, //LXOR
+ // 8, //IINC
+ // 0, //I2L
+ // 0, //I2F
+ // 0, //I2D
+ // 0, //L2I
+ // 0, //L2F
+ // 0, //L2D
+ // 0, //F2I
+ // 0, //F2L
+ // 0, //F2D
+ // 0, //D2I
+ // 0, //D2L
+ // 0, //D2F
+ // 0, //I2B
+ // 0, //I2C
+ // 0, //I2S
+ // 0, //LCMP
+ // 0, //FCMPL
+ // 0, //FCMPG
+ // 0, //DCMPL
+ // 0, //DCMPG
+ // 6, //IFEQ
+ // 6, //IFNE
+ // 6, //IFLT
+ // 6, //IFGE
+ // 6, //IFGT
+ // 6, //IFLE
+ // 6, //IF_ICMPEQ
+ // 6, //IF_ICMPNE
+ // 6, //IF_ICMPLT
+ // 6, //IF_ICMPGE
+ // 6, //IF_ICMPGT
+ // 6, //IF_ICMPLE
+ // 6, //IF_ACMPEQ
+ // 6, //IF_ACMPNE
+ // 6, //GOTO
+ // 6, //JSR
+ // 2, //RET
+ // 9, //TABLESWITCH
+ // 10, //LOOKUPSWITCH
+ // 0, //IRETURN
+ // 0, //LRETURN
+ // 0, //FRETURN
+ // 0, //DRETURN
+ // 0, //ARETURN
+ // 0, //RETURN
+ // 4, //GETSTATIC
+ // 4, //PUTSTATIC
+ // 4, //GETFIELD
+ // 4, //PUTFIELD
+ // 5, //INVOKEVIRTUAL
+ // 5, //INVOKESPECIAL
+ // 5, //INVOKESTATIC
+ // 5, //INVOKEINTERFACE
+ // -1, //UNUSED
+ // 3, //NEW
+ // 1, //NEWARRAY
+ // 3, //ANEWARRAY
+ // 0, //ARRAYLENGTH
+ // 0, //ATHROW
+ // 3, //CHECKCAST
+ // 3, //INSTANCEOF
+ // 0, //MONITORENTER
+ // 0, //MONITOREXIT
+ // -1, //WIDE
+ // 11, //MULTIANEWARRAY
+ // 6, //IFNULL
+ // 6, //IFNONNULL
+ // -1, //GOTO_W
+ // -1 //JSR_W
+ // };
+ // for (int i = 0; i < TYPE.length; ++i) {
+ // System.out.print((char)(TYPE[i] + 1 + 'A'));
+ // }
+ // System.out.println();
+ // }
+
+ /**
+ * Constructs a new {@link CheckMethodAdapter} object.
+ *
+ * @param cv the code visitor to which this adapter must delegate calls.
+ */
+ public CheckMethodAdapter(final MethodVisitor cv) {
+ super(cv);
+ this.labels = new HashMap();
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ checkEndMethod();
+ checkDesc(desc, false);
+ return new CheckAnnotationAdapter(mv.visitAnnotation(desc, visible));
+ }
+
+ public AnnotationVisitor visitAnnotationDefault() {
+ checkEndMethod();
+ return new CheckAnnotationAdapter(mv.visitAnnotationDefault(), false);
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(
+ final int parameter,
+ final String desc,
+ final boolean visible)
+ {
+ checkEndMethod();
+ checkDesc(desc, false);
+ return new CheckAnnotationAdapter(mv.visitParameterAnnotation(parameter,
+ desc,
+ visible));
+ }
+
+ public void visitAttribute(final Attribute attr) {
+ checkEndMethod();
+ if (attr == null) {
+ throw new IllegalArgumentException("Invalid attribute (must not be null)");
+ }
+ mv.visitAttribute(attr);
+ }
+
+ public void visitCode() {
+ startCode = true;
+ mv.visitCode();
+ }
+
+ public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack)
+ {
+ int mLocal;
+ int mStack;
+ switch (type) {
+ case Opcodes.F_NEW:
+ case Opcodes.F_FULL:
+ mLocal = Integer.MAX_VALUE;
+ mStack = Integer.MAX_VALUE;
+ break;
+
+ case Opcodes.F_SAME:
+ mLocal = 0;
+ mStack = 0;
+ break;
+
+ case Opcodes.F_SAME1:
+ mLocal = 0;
+ mStack = 1;
+ break;
+
+ case Opcodes.F_APPEND:
+ case Opcodes.F_CHOP:
+ mLocal = 3;
+ mStack = 0;
+ break;
+
+ default:
+ throw new IllegalArgumentException("Invalid frame type " + type);
+ }
+
+ if (nLocal > mLocal) {
+ throw new IllegalArgumentException("Invalid nLocal=" + nLocal
+ + " for frame type " + type);
+ }
+ if (nStack > mStack) {
+ throw new IllegalArgumentException("Invalid nStack=" + nStack
+ + " for frame type " + type);
+ }
+
+ if (type != Opcodes.F_CHOP) {
+ if (nLocal > 0 && (local == null || local.length < nLocal)) {
+ throw new IllegalArgumentException("Array local[] is shorter than nLocal");
+ }
+ for (int i = 0; i < nLocal; ++i) {
+ checkFrameValue(local[i]);
+ }
+ }
+ if (nStack > 0 && (stack == null || stack.length < nStack)) {
+ throw new IllegalArgumentException("Array stack[] is shorter than nStack");
+ }
+ for (int i = 0; i < nStack; ++i) {
+ checkFrameValue(stack[i]);
+ }
+
+ mv.visitFrame(type, nLocal, local, nStack, stack);
+ }
+
+ public void visitInsn(final int opcode) {
+ checkStartCode();
+ checkEndCode();
+ checkOpcode(opcode, 0);
+ mv.visitInsn(opcode);
+ }
+
+ public void visitIntInsn(final int opcode, final int operand) {
+ checkStartCode();
+ checkEndCode();
+ checkOpcode(opcode, 1);
+ switch (opcode) {
+ case Opcodes.BIPUSH:
+ checkSignedByte(operand, "Invalid operand");
+ break;
+ case Opcodes.SIPUSH:
+ checkSignedShort(operand, "Invalid operand");
+ break;
+ // case Constants.NEWARRAY:
+ default:
+ if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) {
+ throw new IllegalArgumentException("Invalid operand (must be an array type code T_...): "
+ + operand);
+ }
+ }
+ mv.visitIntInsn(opcode, operand);
+ }
+
+ public void visitVarInsn(final int opcode, final int var) {
+ checkStartCode();
+ checkEndCode();
+ checkOpcode(opcode, 2);
+ checkUnsignedShort(var, "Invalid variable index");
+ mv.visitVarInsn(opcode, var);
+ }
+
+ public void visitTypeInsn(final int opcode, final String type) {
+ checkStartCode();
+ checkEndCode();
+ checkOpcode(opcode, 3);
+ checkInternalName(type, "type");
+ if (opcode == Opcodes.NEW && type.charAt(0) == '[') {
+ throw new IllegalArgumentException("NEW cannot be used to create arrays: "
+ + type);
+ }
+ mv.visitTypeInsn(opcode, type);
+ }
+
+ public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ checkStartCode();
+ checkEndCode();
+ checkOpcode(opcode, 4);
+ checkInternalName(owner, "owner");
+ checkIdentifier(name, "name");
+ checkDesc(desc, false);
+ mv.visitFieldInsn(opcode, owner, name, desc);
+ }
+
+ public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ checkStartCode();
+ checkEndCode();
+ checkOpcode(opcode, 5);
+ checkMethodIdentifier(name, "name");
+ checkInternalName(owner, "owner");
+ checkMethodDesc(desc);
+ mv.visitMethodInsn(opcode, owner, name, desc);
+ }
+
+ public void visitJumpInsn(final int opcode, final Label label) {
+ checkStartCode();
+ checkEndCode();
+ checkOpcode(opcode, 6);
+ checkLabel(label, false, "label");
+ mv.visitJumpInsn(opcode, label);
+ }
+
+ public void visitLabel(final Label label) {
+ checkStartCode();
+ checkEndCode();
+ checkLabel(label, false, "label");
+ if (labels.get(label) != null) {
+ throw new IllegalArgumentException("Already visited label");
+ }
+ labels.put(label, new Integer(labels.size()));
+ mv.visitLabel(label);
+ }
+
+ public void visitLdcInsn(final Object cst) {
+ checkStartCode();
+ checkEndCode();
+ if (!(cst instanceof Type)) {
+ checkConstant(cst);
+ }
+ mv.visitLdcInsn(cst);
+ }
+
+ public void visitIincInsn(final int var, final int increment) {
+ checkStartCode();
+ checkEndCode();
+ checkUnsignedShort(var, "Invalid variable index");
+ checkSignedShort(increment, "Invalid increment");
+ mv.visitIincInsn(var, increment);
+ }
+
+ public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label[] labels)
+ {
+ checkStartCode();
+ checkEndCode();
+ if (max < min) {
+ throw new IllegalArgumentException("Max = " + max
+ + " must be greater than or equal to min = " + min);
+ }
+ checkLabel(dflt, false, "default label");
+ if (labels == null || labels.length != max - min + 1) {
+ throw new IllegalArgumentException("There must be max - min + 1 labels");
+ }
+ for (int i = 0; i < labels.length; ++i) {
+ checkLabel(labels[i], false, "label at index " + i);
+ }
+ mv.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+
+ public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int[] keys,
+ final Label[] labels)
+ {
+ checkEndCode();
+ checkStartCode();
+ checkLabel(dflt, false, "default label");
+ if (keys == null || labels == null || keys.length != labels.length) {
+ throw new IllegalArgumentException("There must be the same number of keys and labels");
+ }
+ for (int i = 0; i < labels.length; ++i) {
+ checkLabel(labels[i], false, "label at index " + i);
+ }
+ mv.visitLookupSwitchInsn(dflt, keys, labels);
+ }
+
+ public void visitMultiANewArrayInsn(final String desc, final int dims) {
+ checkStartCode();
+ checkEndCode();
+ checkDesc(desc, false);
+ if (desc.charAt(0) != '[') {
+ throw new IllegalArgumentException("Invalid descriptor (must be an array type descriptor): "
+ + desc);
+ }
+ if (dims < 1) {
+ throw new IllegalArgumentException("Invalid dimensions (must be greater than 0): "
+ + dims);
+ }
+ if (dims > desc.lastIndexOf('[') + 1) {
+ throw new IllegalArgumentException("Invalid dimensions (must not be greater than dims(desc)): "
+ + dims);
+ }
+ mv.visitMultiANewArrayInsn(desc, dims);
+ }
+
+ public void visitTryCatchBlock(
+ final Label start,
+ final Label end,
+ final Label handler,
+ final String type)
+ {
+ checkStartCode();
+ checkEndCode();
+ if (type != null) {
+ checkInternalName(type, "type");
+ }
+ mv.visitTryCatchBlock(start, end, handler, type);
+ }
+
+ public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index)
+ {
+ checkStartCode();
+ checkEndCode();
+ checkIdentifier(name, "name");
+ checkDesc(desc, false);
+ checkLabel(start, true, "start label");
+ checkLabel(end, true, "end label");
+ checkUnsignedShort(index, "Invalid variable index");
+ int s = ((Integer) labels.get(start)).intValue();
+ int e = ((Integer) labels.get(end)).intValue();
+ if (e < s) {
+ throw new IllegalArgumentException("Invalid start and end labels (end must be greater than start)");
+ }
+ mv.visitLocalVariable(name, desc, signature, start, end, index);
+ }
+
+ public void visitLineNumber(final int line, final Label start) {
+ checkStartCode();
+ checkEndCode();
+ checkUnsignedShort(line, "Invalid line number");
+ checkLabel(start, true, "start label");
+ mv.visitLineNumber(line, start);
+ }
+
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ checkStartCode();
+ checkEndCode();
+ endCode = true;
+ checkUnsignedShort(maxStack, "Invalid max stack");
+ checkUnsignedShort(maxLocals, "Invalid max locals");
+ mv.visitMaxs(maxStack, maxLocals);
+ }
+
+ public void visitEnd() {
+ checkEndMethod();
+ endMethod = true;
+ mv.visitEnd();
+ }
+
+ // -------------------------------------------------------------------------
+
+ /**
+ * Checks that the visitCode method has been called.
+ */
+ void checkStartCode() {
+ if (!startCode) {
+ throw new IllegalStateException("Cannot visit instructions before visitCode has been called.");
+ }
+ }
+
+ /**
+ * Checks that the visitMaxs method has not been called.
+ */
+ void checkEndCode() {
+ if (endCode) {
+ throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called.");
+ }
+ }
+
+ /**
+ * Checks that the visitEnd method has not been called.
+ */
+ void checkEndMethod() {
+ if (endMethod) {
+ throw new IllegalStateException("Cannot visit elements after visitEnd has been called.");
+ }
+ }
+
+ /**
+ * Checks a stack frame value.
+ *
+ * @param value the value to be checked.
+ */
+ static void checkFrameValue(final Object value) {
+ if (value == Opcodes.TOP || value == Opcodes.INTEGER
+ || value == Opcodes.FLOAT || value == Opcodes.LONG
+ || value == Opcodes.DOUBLE || value == Opcodes.NULL
+ || value == Opcodes.UNINITIALIZED_THIS)
+ {
+ return;
+ }
+ if (value instanceof String) {
+ checkInternalName((String) value, "Invalid stack frame value");
+ return;
+ }
+ if (!(value instanceof Label)) {
+ throw new IllegalArgumentException("Invalid stack frame value: "
+ + value);
+ }
+ }
+
+ /**
+ * Checks that the type of the given opcode is equal to the given type.
+ *
+ * @param opcode the opcode to be checked.
+ * @param type the expected opcode type.
+ */
+ static void checkOpcode(final int opcode, final int type) {
+ if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) {
+ throw new IllegalArgumentException("Invalid opcode: " + opcode);
+ }
+ }
+
+ /**
+ * Checks that the given value is a signed byte.
+ *
+ * @param value the value to be checked.
+ * @param msg an message to be used in case of error.
+ */
+ static void checkSignedByte(final int value, final String msg) {
+ if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
+ throw new IllegalArgumentException(msg
+ + " (must be a signed byte): " + value);
+ }
+ }
+
+ /**
+ * Checks that the given value is a signed short.
+ *
+ * @param value the value to be checked.
+ * @param msg an message to be used in case of error.
+ */
+ static void checkSignedShort(final int value, final String msg) {
+ if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
+ throw new IllegalArgumentException(msg
+ + " (must be a signed short): " + value);
+ }
+ }
+
+ /**
+ * Checks that the given value is an unsigned short.
+ *
+ * @param value the value to be checked.
+ * @param msg an message to be used in case of error.
+ */
+ static void checkUnsignedShort(final int value, final String msg) {
+ if (value < 0 || value > 65535) {
+ throw new IllegalArgumentException(msg
+ + " (must be an unsigned short): " + value);
+ }
+ }
+
+ /**
+ * Checks that the given value is an {@link Integer}, a{@link Float}, a
+ * {@link Long}, a {@link Double} or a {@link String}.
+ *
+ * @param cst the value to be checked.
+ */
+ static void checkConstant(final Object cst) {
+ if (!(cst instanceof Integer) && !(cst instanceof Float)
+ && !(cst instanceof Long) && !(cst instanceof Double)
+ && !(cst instanceof String))
+ {
+ throw new IllegalArgumentException("Invalid constant: " + cst);
+ }
+ }
+
+ /**
+ * Checks that the given string is a valid Java identifier.
+ *
+ * @param name the string to be checked.
+ * @param msg a message to be used in case of error.
+ */
+ static void checkIdentifier(final String name, final String msg) {
+ checkIdentifier(name, 0, -1, msg);
+ }
+
+ /**
+ * Checks that the given substring is a valid Java identifier.
+ *
+ * @param name the string to be checked.
+ * @param start index of the first character of the identifier (inclusive).
+ * @param end index of the last character of the identifier (exclusive). -1
+ * is equivalent to <tt>name.length()</tt> if name is not
+ * <tt>null</tt>.
+ * @param msg a message to be used in case of error.
+ */
+ static void checkIdentifier(
+ final String name,
+ final int start,
+ final int end,
+ final String msg)
+ {
+ if (name == null || (end == -1 ? name.length() <= start : end <= start))
+ {
+ throw new IllegalArgumentException("Invalid " + msg
+ + " (must not be null or empty)");
+ }
+ if (!Character.isJavaIdentifierStart(name.charAt(start))) {
+ throw new IllegalArgumentException("Invalid " + msg
+ + " (must be a valid Java identifier): " + name);
+ }
+ int max = end == -1 ? name.length() : end;
+ for (int i = start + 1; i < max; ++i) {
+ if (!Character.isJavaIdentifierPart(name.charAt(i))) {
+ throw new IllegalArgumentException("Invalid " + msg
+ + " (must be a valid Java identifier): " + name);
+ }
+ }
+ }
+
+ /**
+ * Checks that the given string is a valid Java identifier or is equal to
+ * '&lt;init&gt;' or '&lt;clinit&gt;'.
+ *
+ * @param name the string to be checked.
+ * @param msg a message to be used in case of error.
+ */
+ static void checkMethodIdentifier(final String name, final String msg) {
+ if (name == null || name.length() == 0) {
+ throw new IllegalArgumentException("Invalid " + msg
+ + " (must not be null or empty)");
+ }
+ if ("<init>".equals(name) || "<clinit>".equals(name)) {
+ return;
+ }
+ if (!Character.isJavaIdentifierStart(name.charAt(0))) {
+ throw new IllegalArgumentException("Invalid "
+ + msg
+ + " (must be a '<init>', '<clinit>' or a valid Java identifier): "
+ + name);
+ }
+ for (int i = 1; i < name.length(); ++i) {
+ if (!Character.isJavaIdentifierPart(name.charAt(i))) {
+ throw new IllegalArgumentException("Invalid "
+ + msg
+ + " (must be '<init>' or '<clinit>' or a valid Java identifier): "
+ + name);
+ }
+ }
+ }
+
+ /**
+ * Checks that the given string is a valid internal class name.
+ *
+ * @param name the string to be checked.
+ * @param msg a message to be used in case of error.
+ */
+ static void checkInternalName(final String name, final String msg) {
+ if (name == null || name.length() == 0) {
+ throw new IllegalArgumentException("Invalid " + msg
+ + " (must not be null or empty)");
+ }
+ if (name.charAt(0) == '[') {
+ checkDesc(name, false);
+ } else {
+ checkInternalName(name, 0, -1, msg);
+ }
+ }
+
+ /**
+ * Checks that the given substring is a valid internal class name.
+ *
+ * @param name the string to be checked.
+ * @param start index of the first character of the identifier (inclusive).
+ * @param end index of the last character of the identifier (exclusive). -1
+ * is equivalent to <tt>name.length()</tt> if name is not
+ * <tt>null</tt>.
+ * @param msg a message to be used in case of error.
+ */
+ static void checkInternalName(
+ final String name,
+ final int start,
+ final int end,
+ final String msg)
+ {
+ int max = end == -1 ? name.length() : end;
+ try {
+ int begin = start;
+ int slash;
+ do {
+ slash = name.indexOf('/', begin + 1);
+ if (slash == -1 || slash > max) {
+ slash = max;
+ }
+ checkIdentifier(name, begin, slash, null);
+ begin = slash + 1;
+ } while (slash != max);
+ } catch (IllegalArgumentException _) {
+ throw new IllegalArgumentException("Invalid "
+ + msg
+ + " (must be a fully qualified class name in internal form): "
+ + name);
+ }
+ }
+
+ /**
+ * Checks that the given string is a valid type descriptor.
+ *
+ * @param desc the string to be checked.
+ * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
+ */
+ static void checkDesc(final String desc, final boolean canBeVoid) {
+ int end = checkDesc(desc, 0, canBeVoid);
+ if (end != desc.length()) {
+ throw new IllegalArgumentException("Invalid descriptor: " + desc);
+ }
+ }
+
+ /**
+ * Checks that a the given substring is a valid type descriptor.
+ *
+ * @param desc the string to be checked.
+ * @param start index of the first character of the identifier (inclusive).
+ * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
+ * @return the index of the last character of the type decriptor, plus one.
+ */
+ static int checkDesc(
+ final String desc,
+ final int start,
+ final boolean canBeVoid)
+ {
+ if (desc == null || start >= desc.length()) {
+ throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)");
+ }
+ int index;
+ switch (desc.charAt(start)) {
+ case 'V':
+ if (canBeVoid) {
+ return start + 1;
+ } else {
+ throw new IllegalArgumentException("Invalid descriptor: "
+ + desc);
+ }
+ case 'Z':
+ case 'C':
+ case 'B':
+ case 'S':
+ case 'I':
+ case 'F':
+ case 'J':
+ case 'D':
+ return start + 1;
+ case '[':
+ index = start + 1;
+ while (index < desc.length() && desc.charAt(index) == '[') {
+ ++index;
+ }
+ if (index < desc.length()) {
+ return checkDesc(desc, index, false);
+ } else {
+ throw new IllegalArgumentException("Invalid descriptor: "
+ + desc);
+ }
+ case 'L':
+ index = desc.indexOf(';', start);
+ if (index == -1 || index - start < 2) {
+ throw new IllegalArgumentException("Invalid descriptor: "
+ + desc);
+ }
+ try {
+ checkInternalName(desc, start + 1, index, null);
+ } catch (IllegalArgumentException _) {
+ throw new IllegalArgumentException("Invalid descriptor: "
+ + desc);
+ }
+ return index + 1;
+ default:
+ throw new IllegalArgumentException("Invalid descriptor: "
+ + desc);
+ }
+ }
+
+ /**
+ * Checks that the given string is a valid method descriptor.
+ *
+ * @param desc the string to be checked.
+ */
+ static void checkMethodDesc(final String desc) {
+ if (desc == null || desc.length() == 0) {
+ throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)");
+ }
+ if (desc.charAt(0) != '(' || desc.length() < 3) {
+ throw new IllegalArgumentException("Invalid descriptor: " + desc);
+ }
+ int start = 1;
+ if (desc.charAt(start) != ')') {
+ do {
+ if (desc.charAt(start) == 'V') {
+ throw new IllegalArgumentException("Invalid descriptor: "
+ + desc);
+ }
+ start = checkDesc(desc, start, false);
+ } while (start < desc.length() && desc.charAt(start) != ')');
+ }
+ start = checkDesc(desc, start + 1, true);
+ if (start != desc.length()) {
+ throw new IllegalArgumentException("Invalid descriptor: " + desc);
+ }
+ }
+
+ /**
+ * Checks a class signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ */
+ static void checkClassSignature(final String signature) {
+ // ClassSignature:
+ // FormalTypeParameters? ClassTypeSignature ClassTypeSignature*
+
+ int pos = 0;
+ if (getChar(signature, 0) == '<') {
+ pos = checkFormalTypeParameters(signature, pos);
+ }
+ pos = checkClassTypeSignature(signature, pos);
+ while (getChar(signature, pos) == 'L') {
+ pos = checkClassTypeSignature(signature, pos);
+ }
+ if (pos != signature.length()) {
+ throw new IllegalArgumentException(signature + ": error at index "
+ + pos);
+ }
+ }
+
+ /**
+ * Checks a method signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ */
+ static void checkMethodSignature(final String signature) {
+ // MethodTypeSignature:
+ // FormalTypeParameters? ( TypeSignature* ) ( TypeSignature | V ) (
+ // ^ClassTypeSignature | ^TypeVariableSignature )*
+
+ int pos = 0;
+ if (getChar(signature, 0) == '<') {
+ pos = checkFormalTypeParameters(signature, pos);
+ }
+ pos = checkChar('(', signature, pos);
+ while ("ZCBSIFJDL[T".indexOf(getChar(signature, pos)) != -1) {
+ pos = checkTypeSignature(signature, pos);
+ }
+ pos = checkChar(')', signature, pos);
+ if (getChar(signature, pos) == 'V') {
+ ++pos;
+ } else {
+ pos = checkTypeSignature(signature, pos);
+ }
+ while (getChar(signature, pos) == '^') {
+ ++pos;
+ if (getChar(signature, pos) == 'L') {
+ pos = checkClassTypeSignature(signature, pos);
+ } else {
+ pos = checkTypeVariableSignature(signature, pos);
+ }
+ }
+ if (pos != signature.length()) {
+ throw new IllegalArgumentException(signature + ": error at index "
+ + pos);
+ }
+ }
+
+ /**
+ * Checks a field signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ */
+ static void checkFieldSignature(final String signature) {
+ int pos = checkFieldTypeSignature(signature, 0);
+ if (pos != signature.length()) {
+ throw new IllegalArgumentException(signature + ": error at index "
+ + pos);
+ }
+ }
+
+ /**
+ * Checks the formal type parameters of a class or method signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkFormalTypeParameters(final String signature, int pos)
+ {
+ // FormalTypeParameters:
+ // < FormalTypeParameter+ >
+
+ pos = checkChar('<', signature, pos);
+ pos = checkFormalTypeParameter(signature, pos);
+ while (getChar(signature, pos) != '>') {
+ pos = checkFormalTypeParameter(signature, pos);
+ }
+ return pos + 1;
+ }
+
+ /**
+ * Checks a formal type parameter of a class or method signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkFormalTypeParameter(final String signature, int pos)
+ {
+ // FormalTypeParameter:
+ // Identifier : FieldTypeSignature? (: FieldTypeSignature)*
+
+ pos = checkIdentifier(signature, pos);
+ pos = checkChar(':', signature, pos);
+ if ("L[T".indexOf(getChar(signature, pos)) != -1) {
+ pos = checkFieldTypeSignature(signature, pos);
+ }
+ while (getChar(signature, pos) == ':') {
+ pos = checkFieldTypeSignature(signature, pos + 1);
+ }
+ return pos;
+ }
+
+ /**
+ * Checks a field type signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkFieldTypeSignature(final String signature, int pos)
+ {
+ // FieldTypeSignature:
+ // ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature
+ //
+ // ArrayTypeSignature:
+ // [ TypeSignature
+
+ switch (getChar(signature, pos)) {
+ case 'L':
+ return checkClassTypeSignature(signature, pos);
+ case '[':
+ return checkTypeSignature(signature, pos + 1);
+ default:
+ return checkTypeVariableSignature(signature, pos);
+ }
+ }
+
+ /**
+ * Checks a class type signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkClassTypeSignature(final String signature, int pos)
+ {
+ // ClassTypeSignature:
+ // L Identifier ( / Identifier )* TypeArguments? ( . Identifier
+ // TypeArguments? )* ;
+
+ pos = checkChar('L', signature, pos);
+ pos = checkIdentifier(signature, pos);
+ while (getChar(signature, pos) == '/') {
+ pos = checkIdentifier(signature, pos + 1);
+ }
+ if (getChar(signature, pos) == '<') {
+ pos = checkTypeArguments(signature, pos);
+ }
+ while (getChar(signature, pos) == '.') {
+ pos = checkIdentifier(signature, pos + 1);
+ if (getChar(signature, pos) == '<') {
+ pos = checkTypeArguments(signature, pos);
+ }
+ }
+ return checkChar(';', signature, pos);
+ }
+
+ /**
+ * Checks the type arguments in a class type signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkTypeArguments(final String signature, int pos) {
+ // TypeArguments:
+ // < TypeArgument+ >
+
+ pos = checkChar('<', signature, pos);
+ pos = checkTypeArgument(signature, pos);
+ while (getChar(signature, pos) != '>') {
+ pos = checkTypeArgument(signature, pos);
+ }
+ return pos + 1;
+ }
+
+ /**
+ * Checks a type argument in a class type signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkTypeArgument(final String signature, int pos) {
+ // TypeArgument:
+ // * | ( ( + | - )? FieldTypeSignature )
+
+ char c = getChar(signature, pos);
+ if (c == '*') {
+ return pos + 1;
+ } else if (c == '+' || c == '-') {
+ pos++;
+ }
+ return checkFieldTypeSignature(signature, pos);
+ }
+
+ /**
+ * Checks a type variable signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkTypeVariableSignature(
+ final String signature,
+ int pos)
+ {
+ // TypeVariableSignature:
+ // T Identifier ;
+
+ pos = checkChar('T', signature, pos);
+ pos = checkIdentifier(signature, pos);
+ return checkChar(';', signature, pos);
+ }
+
+ /**
+ * Checks a type signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkTypeSignature(final String signature, int pos) {
+ // TypeSignature:
+ // Z | C | B | S | I | F | J | D | FieldTypeSignature
+
+ switch (getChar(signature, pos)) {
+ case 'Z':
+ case 'C':
+ case 'B':
+ case 'S':
+ case 'I':
+ case 'F':
+ case 'J':
+ case 'D':
+ return pos + 1;
+ default:
+ return checkFieldTypeSignature(signature, pos);
+ }
+ }
+
+ /**
+ * Checks an identifier.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkIdentifier(final String signature, int pos) {
+ if (!Character.isJavaIdentifierStart(getChar(signature, pos))) {
+ throw new IllegalArgumentException(signature
+ + ": identifier expected at index " + pos);
+ }
+ ++pos;
+ while (Character.isJavaIdentifierPart(getChar(signature, pos))) {
+ ++pos;
+ }
+ return pos;
+ }
+
+ /**
+ * Checks a single character.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkChar(final char c, final String signature, int pos)
+ {
+ if (getChar(signature, pos) == c) {
+ return pos + 1;
+ }
+ throw new IllegalArgumentException(signature + ": '" + c
+ + "' expected at index " + pos);
+ }
+
+ /**
+ * Returns the signature car at the given index.
+ *
+ * @param signature a signature.
+ * @param pos an index in signature.
+ * @return the character at the given index, or 0 if there is no such
+ * character.
+ */
+ private static char getChar(final String signature, int pos) {
+ return pos < signature.length() ? signature.charAt(pos) : (char) 0;
+ }
+
+ /**
+ * Checks that the given label is not null. This method can also check that
+ * the label has been visited.
+ *
+ * @param label the label to be checked.
+ * @param checkVisited <tt>true</tt> to check that the label has been
+ * visited.
+ * @param msg a message to be used in case of error.
+ */
+ void checkLabel(
+ final Label label,
+ final boolean checkVisited,
+ final String msg)
+ {
+ if (label == null) {
+ throw new IllegalArgumentException("Invalid " + msg
+ + " (must not be null)");
+ }
+ if (checkVisited && labels.get(label) == null) {
+ throw new IllegalArgumentException("Invalid " + msg
+ + " (must be visited first)");
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/CheckSignatureAdapter.java b/cglib-and-asm/src/org/mockito/asm/util/CheckSignatureAdapter.java
new file mode 100644
index 0000000..05e9afb
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/CheckSignatureAdapter.java
@@ -0,0 +1,290 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.signature.SignatureVisitor;
+
+/**
+ * A {@link SignatureVisitor} that checks that its methods are properly used.
+ *
+ * @author Eric Bruneton
+ */
+public class CheckSignatureAdapter implements SignatureVisitor {
+
+ /**
+ * Type to be used to check class signatures. See
+ * {@link #CheckSignatureAdapter(int, SignatureVisitor) CheckSignatureAdapter}.
+ */
+ public static final int CLASS_SIGNATURE = 0;
+
+ /**
+ * Type to be used to check method signatures. See
+ * {@link #CheckSignatureAdapter(int, SignatureVisitor) CheckSignatureAdapter}.
+ */
+ public static final int METHOD_SIGNATURE = 1;
+
+ /**
+ * Type to be used to check type signatures.See
+ * {@link #CheckSignatureAdapter(int, SignatureVisitor) CheckSignatureAdapter}.
+ */
+ public static final int TYPE_SIGNATURE = 2;
+
+ private static final int EMPTY = 1;
+
+ private static final int FORMAL = 2;
+
+ private static final int BOUND = 4;
+
+ private static final int SUPER = 8;
+
+ private static final int PARAM = 16;
+
+ private static final int RETURN = 32;
+
+ private static final int SIMPLE_TYPE = 64;
+
+ private static final int CLASS_TYPE = 128;
+
+ private static final int END = 256;
+
+ /**
+ * Type of the signature to be checked.
+ */
+ private final int type;
+
+ /**
+ * State of the automaton used to check the order of method calls.
+ */
+ private int state;
+
+ /**
+ * <tt>true</tt> if the checked type signature can be 'V'.
+ */
+ private boolean canBeVoid;
+
+ /**
+ * The visitor to which this adapter must delegate calls. May be
+ * <tt>null</tt>.
+ */
+ private final SignatureVisitor sv;
+
+ /**
+ * Creates a new {@link CheckSignatureAdapter} object.
+ *
+ * @param type the type of signature to be checked. See
+ * {@link #CLASS_SIGNATURE}, {@link #METHOD_SIGNATURE} and
+ * {@link #TYPE_SIGNATURE}.
+ * @param sv the visitor to which this adapter must delegate calls. May be
+ * <tt>null</tt>.
+ */
+ public CheckSignatureAdapter(final int type, final SignatureVisitor sv) {
+ this.type = type;
+ this.state = EMPTY;
+ this.sv = sv;
+ }
+
+ // class and method signatures
+
+ public void visitFormalTypeParameter(final String name) {
+ if (type == TYPE_SIGNATURE
+ || (state != EMPTY && state != FORMAL && state != BOUND))
+ {
+ throw new IllegalStateException();
+ }
+ CheckMethodAdapter.checkIdentifier(name, "formal type parameter");
+ state = FORMAL;
+ if (sv != null) {
+ sv.visitFormalTypeParameter(name);
+ }
+ }
+
+ public SignatureVisitor visitClassBound() {
+ if (state != FORMAL) {
+ throw new IllegalStateException();
+ }
+ state = BOUND;
+ SignatureVisitor v = sv == null ? null : sv.visitClassBound();
+ return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
+ }
+
+ public SignatureVisitor visitInterfaceBound() {
+ if (state != FORMAL && state != BOUND) {
+ throw new IllegalArgumentException();
+ }
+ SignatureVisitor v = sv == null ? null : sv.visitInterfaceBound();
+ return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
+ }
+
+ // class signatures
+
+ public SignatureVisitor visitSuperclass() {
+ if (type != CLASS_SIGNATURE || (state & (EMPTY | FORMAL | BOUND)) == 0)
+ {
+ throw new IllegalArgumentException();
+ }
+ state = SUPER;
+ SignatureVisitor v = sv == null ? null : sv.visitSuperclass();
+ return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
+ }
+
+ public SignatureVisitor visitInterface() {
+ if (state != SUPER) {
+ throw new IllegalStateException();
+ }
+ SignatureVisitor v = sv == null ? null : sv.visitInterface();
+ return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
+ }
+
+ // method signatures
+
+ public SignatureVisitor visitParameterType() {
+ if (type != METHOD_SIGNATURE
+ || (state & (EMPTY | FORMAL | BOUND | PARAM)) == 0)
+ {
+ throw new IllegalArgumentException();
+ }
+ state = PARAM;
+ SignatureVisitor v = sv == null ? null : sv.visitParameterType();
+ return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
+ }
+
+ public SignatureVisitor visitReturnType() {
+ if (type != METHOD_SIGNATURE
+ || (state & (EMPTY | FORMAL | BOUND | PARAM)) == 0)
+ {
+ throw new IllegalArgumentException();
+ }
+ state = RETURN;
+ SignatureVisitor v = sv == null ? null : sv.visitReturnType();
+ CheckSignatureAdapter cv = new CheckSignatureAdapter(TYPE_SIGNATURE, v);
+ cv.canBeVoid = true;
+ return cv;
+ }
+
+ public SignatureVisitor visitExceptionType() {
+ if (state != RETURN) {
+ throw new IllegalStateException();
+ }
+ SignatureVisitor v = sv == null ? null : sv.visitExceptionType();
+ return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
+ }
+
+ // type signatures
+
+ public void visitBaseType(final char descriptor) {
+ if (type != TYPE_SIGNATURE || state != EMPTY) {
+ throw new IllegalStateException();
+ }
+ if (descriptor == 'V') {
+ if (!canBeVoid) {
+ throw new IllegalArgumentException();
+ }
+ } else {
+ if ("ZCBSIFJD".indexOf(descriptor) == -1) {
+ throw new IllegalArgumentException();
+ }
+ }
+ state = SIMPLE_TYPE;
+ if (sv != null) {
+ sv.visitBaseType(descriptor);
+ }
+ }
+
+ public void visitTypeVariable(final String name) {
+ if (type != TYPE_SIGNATURE || state != EMPTY) {
+ throw new IllegalStateException();
+ }
+ CheckMethodAdapter.checkIdentifier(name, "type variable");
+ state = SIMPLE_TYPE;
+ if (sv != null) {
+ sv.visitTypeVariable(name);
+ }
+ }
+
+ public SignatureVisitor visitArrayType() {
+ if (type != TYPE_SIGNATURE || state != EMPTY) {
+ throw new IllegalStateException();
+ }
+ state = SIMPLE_TYPE;
+ SignatureVisitor v = sv == null ? null : sv.visitArrayType();
+ return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
+ }
+
+ public void visitClassType(final String name) {
+ if (type != TYPE_SIGNATURE || state != EMPTY) {
+ throw new IllegalStateException();
+ }
+ CheckMethodAdapter.checkInternalName(name, "class name");
+ state = CLASS_TYPE;
+ if (sv != null) {
+ sv.visitClassType(name);
+ }
+ }
+
+ public void visitInnerClassType(final String name) {
+ if (state != CLASS_TYPE) {
+ throw new IllegalStateException();
+ }
+ CheckMethodAdapter.checkIdentifier(name, "inner class name");
+ if (sv != null) {
+ sv.visitInnerClassType(name);
+ }
+ }
+
+ public void visitTypeArgument() {
+ if (state != CLASS_TYPE) {
+ throw new IllegalStateException();
+ }
+ if (sv != null) {
+ sv.visitTypeArgument();
+ }
+ }
+
+ public SignatureVisitor visitTypeArgument(final char wildcard) {
+ if (state != CLASS_TYPE) {
+ throw new IllegalStateException();
+ }
+ if ("+-=".indexOf(wildcard) == -1) {
+ throw new IllegalArgumentException();
+ }
+ SignatureVisitor v = sv == null ? null : sv.visitTypeArgument(wildcard);
+ return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
+ }
+
+ public void visitEnd() {
+ if (state != CLASS_TYPE) {
+ throw new IllegalStateException();
+ }
+ state = END;
+ if (sv != null) {
+ sv.visitEnd();
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/TraceAbstractVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/TraceAbstractVisitor.java
new file mode 100644
index 0000000..b7173fd
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/TraceAbstractVisitor.java
@@ -0,0 +1,179 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Attribute;
+
+/**
+ * An abstract trace visitor.
+ *
+ * @author Eric Bruneton
+ */
+public abstract class TraceAbstractVisitor extends AbstractVisitor {
+
+ /**
+ * Constant used in {@link #appendDescriptor appendDescriptor} for internal
+ * type names in bytecode notation.
+ */
+ public static final int INTERNAL_NAME = 0;
+
+ /**
+ * Constant used in {@link #appendDescriptor appendDescriptor} for field
+ * descriptors, formatted in bytecode notation
+ */
+ public static final int FIELD_DESCRIPTOR = 1;
+
+ /**
+ * Constant used in {@link #appendDescriptor appendDescriptor} for field
+ * signatures, formatted in bytecode notation
+ */
+ public static final int FIELD_SIGNATURE = 2;
+
+ /**
+ * Constant used in {@link #appendDescriptor appendDescriptor} for method
+ * descriptors, formatted in bytecode notation
+ */
+ public static final int METHOD_DESCRIPTOR = 3;
+
+ /**
+ * Constant used in {@link #appendDescriptor appendDescriptor} for method
+ * signatures, formatted in bytecode notation
+ */
+ public static final int METHOD_SIGNATURE = 4;
+
+ /**
+ * Constant used in {@link #appendDescriptor appendDescriptor} for class
+ * signatures, formatted in bytecode notation
+ */
+ public static final int CLASS_SIGNATURE = 5;
+
+ /**
+ * Constant used in {@link #appendDescriptor appendDescriptor} for field or
+ * method return value signatures, formatted in default Java notation
+ * (non-bytecode)
+ */
+ public static final int TYPE_DECLARATION = 6;
+
+ /**
+ * Constant used in {@link #appendDescriptor appendDescriptor} for class
+ * signatures, formatted in default Java notation (non-bytecode)
+ */
+ public static final int CLASS_DECLARATION = 7;
+
+ /**
+ * Constant used in {@link #appendDescriptor appendDescriptor} for method
+ * parameter signatures, formatted in default Java notation (non-bytecode)
+ */
+ public static final int PARAMETERS_DECLARATION = 8;
+
+ /**
+ * Tab for class members.
+ */
+ protected String tab = " ";
+
+ /**
+ * Prints a disassembled view of the given annotation.
+ *
+ * @param desc the class descriptor of the annotation class.
+ * @param visible <tt>true</tt> if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values.
+ */
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ buf.setLength(0);
+ buf.append(tab).append('@');
+ appendDescriptor(FIELD_DESCRIPTOR, desc);
+ buf.append('(');
+ text.add(buf.toString());
+ TraceAnnotationVisitor tav = createTraceAnnotationVisitor();
+ text.add(tav.getText());
+ text.add(visible ? ")\n" : ") // invisible\n");
+ return tav;
+ }
+
+ /**
+ * Prints a disassembled view of the given attribute.
+ *
+ * @param attr an attribute.
+ */
+ public void visitAttribute(final Attribute attr) {
+ buf.setLength(0);
+ buf.append(tab).append("ATTRIBUTE ");
+ appendDescriptor(-1, attr.type);
+
+ if (attr instanceof Traceable) {
+ ((Traceable) attr).trace(buf, null);
+ } else {
+ buf.append(" : unknown\n");
+ }
+
+ text.add(buf.toString());
+ }
+
+ /**
+ * Does nothing.
+ */
+ public void visitEnd() {
+ // does nothing
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods
+ // ------------------------------------------------------------------------
+
+ protected TraceAnnotationVisitor createTraceAnnotationVisitor() {
+ return new TraceAnnotationVisitor();
+ }
+
+ /**
+ * Appends an internal name, a type descriptor or a type signature to
+ * {@link #buf buf}.
+ *
+ * @param type indicates if desc is an internal name, a field descriptor, a
+ * method descriptor, a class signature, ...
+ * @param desc an internal name, type descriptor, or type signature. May be
+ * <tt>null</tt>.
+ */
+ protected void appendDescriptor(final int type, final String desc) {
+ if (type == CLASS_SIGNATURE || type == FIELD_SIGNATURE
+ || type == METHOD_SIGNATURE)
+ {
+ if (desc != null) {
+ buf.append("// signature ").append(desc).append('\n');
+ }
+ } else {
+ buf.append(desc);
+ }
+ }
+
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/TraceAnnotationVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/TraceAnnotationVisitor.java
new file mode 100644
index 0000000..918f88a
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/TraceAnnotationVisitor.java
@@ -0,0 +1,266 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Type;
+
+/**
+ * An {@link AnnotationVisitor} that prints a disassembled view of the
+ * annotations it visits.
+ *
+ * @author Eric Bruneton
+ */
+public class TraceAnnotationVisitor extends TraceAbstractVisitor implements
+ AnnotationVisitor
+{
+
+ /**
+ * The {@link AnnotationVisitor} to which this visitor delegates calls. May
+ * be <tt>null</tt>.
+ */
+ protected AnnotationVisitor av;
+
+ private int valueNumber = 0;
+
+ /**
+ * Constructs a new {@link TraceAnnotationVisitor}.
+ */
+ public TraceAnnotationVisitor() {
+ // ignore
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the AnnotationVisitor interface
+ // ------------------------------------------------------------------------
+
+ public void visit(final String name, final Object value) {
+ buf.setLength(0);
+ appendComa(valueNumber++);
+
+ if (name != null) {
+ buf.append(name).append('=');
+ }
+
+ if (value instanceof String) {
+ visitString((String) value);
+ } else if (value instanceof Type) {
+ visitType((Type) value);
+ } else if (value instanceof Byte) {
+ visitByte(((Byte) value).byteValue());
+ } else if (value instanceof Boolean) {
+ visitBoolean(((Boolean) value).booleanValue());
+ } else if (value instanceof Short) {
+ visitShort(((Short) value).shortValue());
+ } else if (value instanceof Character) {
+ visitChar(((Character) value).charValue());
+ } else if (value instanceof Integer) {
+ visitInt(((Integer) value).intValue());
+ } else if (value instanceof Float) {
+ visitFloat(((Float) value).floatValue());
+ } else if (value instanceof Long) {
+ visitLong(((Long) value).longValue());
+ } else if (value instanceof Double) {
+ visitDouble(((Double) value).doubleValue());
+ } else if (value.getClass().isArray()) {
+ buf.append('{');
+ if (value instanceof byte[]) {
+ byte[] v = (byte[]) value;
+ for (int i = 0; i < v.length; i++) {
+ appendComa(i);
+ visitByte(v[i]);
+ }
+ } else if (value instanceof boolean[]) {
+ boolean[] v = (boolean[]) value;
+ for (int i = 0; i < v.length; i++) {
+ appendComa(i);
+ visitBoolean(v[i]);
+ }
+ } else if (value instanceof short[]) {
+ short[] v = (short[]) value;
+ for (int i = 0; i < v.length; i++) {
+ appendComa(i);
+ visitShort(v[i]);
+ }
+ } else if (value instanceof char[]) {
+ char[] v = (char[]) value;
+ for (int i = 0; i < v.length; i++) {
+ appendComa(i);
+ visitChar(v[i]);
+ }
+ } else if (value instanceof int[]) {
+ int[] v = (int[]) value;
+ for (int i = 0; i < v.length; i++) {
+ appendComa(i);
+ visitInt(v[i]);
+ }
+ } else if (value instanceof long[]) {
+ long[] v = (long[]) value;
+ for (int i = 0; i < v.length; i++) {
+ appendComa(i);
+ visitLong(v[i]);
+ }
+ } else if (value instanceof float[]) {
+ float[] v = (float[]) value;
+ for (int i = 0; i < v.length; i++) {
+ appendComa(i);
+ visitFloat(v[i]);
+ }
+ } else if (value instanceof double[]) {
+ double[] v = (double[]) value;
+ for (int i = 0; i < v.length; i++) {
+ appendComa(i);
+ visitDouble(v[i]);
+ }
+ }
+ buf.append('}');
+ }
+
+ text.add(buf.toString());
+
+ if (av != null) {
+ av.visit(name, value);
+ }
+ }
+
+ private void visitInt(final int value) {
+ buf.append(value);
+ }
+
+ private void visitLong(final long value) {
+ buf.append(value).append('L');
+ }
+
+ private void visitFloat(final float value) {
+ buf.append(value).append('F');
+ }
+
+ private void visitDouble(final double value) {
+ buf.append(value).append('D');
+ }
+
+ private void visitChar(final char value) {
+ buf.append("(char)").append((int) value);
+ }
+
+ private void visitShort(final short value) {
+ buf.append("(short)").append(value);
+ }
+
+ private void visitByte(final byte value) {
+ buf.append("(byte)").append(value);
+ }
+
+ private void visitBoolean(final boolean value) {
+ buf.append(value);
+ }
+
+ private void visitString(final String value) {
+ appendString(buf, value);
+ }
+
+ private void visitType(final Type value) {
+ buf.append(value.getClassName()).append(".class");
+ }
+
+ public void visitEnum(
+ final String name,
+ final String desc,
+ final String value)
+ {
+ buf.setLength(0);
+ appendComa(valueNumber++);
+ if (name != null) {
+ buf.append(name).append('=');
+ }
+ appendDescriptor(FIELD_DESCRIPTOR, desc);
+ buf.append('.').append(value);
+ text.add(buf.toString());
+
+ if (av != null) {
+ av.visitEnum(name, desc, value);
+ }
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String name,
+ final String desc)
+ {
+ buf.setLength(0);
+ appendComa(valueNumber++);
+ if (name != null) {
+ buf.append(name).append('=');
+ }
+ buf.append('@');
+ appendDescriptor(FIELD_DESCRIPTOR, desc);
+ buf.append('(');
+ text.add(buf.toString());
+ TraceAnnotationVisitor tav = createTraceAnnotationVisitor();
+ text.add(tav.getText());
+ text.add(")");
+ if (av != null) {
+ tav.av = av.visitAnnotation(name, desc);
+ }
+ return tav;
+ }
+
+ public AnnotationVisitor visitArray(final String name) {
+ buf.setLength(0);
+ appendComa(valueNumber++);
+ if (name != null) {
+ buf.append(name).append('=');
+ }
+ buf.append('{');
+ text.add(buf.toString());
+ TraceAnnotationVisitor tav = createTraceAnnotationVisitor();
+ text.add(tav.getText());
+ text.add("}");
+ if (av != null) {
+ tav.av = av.visitArray(name);
+ }
+ return tav;
+ }
+
+ public void visitEnd() {
+ if (av != null) {
+ av.visitEnd();
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods
+ // ------------------------------------------------------------------------
+
+ private void appendComa(final int i) {
+ if (i != 0) {
+ buf.append(", ");
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/TraceClassVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/TraceClassVisitor.java
new file mode 100644
index 0000000..aec353e
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/TraceClassVisitor.java
@@ -0,0 +1,523 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import java.io.FileInputStream;
+import java.io.PrintWriter;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Attribute;
+import org.mockito.asm.ClassReader;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.FieldVisitor;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+import org.mockito.asm.signature.SignatureReader;
+
+/**
+ * A {@link ClassVisitor} that prints a disassembled view of the classes it
+ * visits. This class visitor can be used alone (see the {@link #main main}
+ * method) to disassemble a class. It can also be used in the middle of class
+ * visitor chain to trace the class that is visited at a given point in this
+ * chain. This may be uselful for debugging purposes. <p> The trace printed when
+ * visiting the <tt>Hello</tt> class is the following: <p> <blockquote>
+ *
+ * <pre>
+ * // class version 49.0 (49)
+ * // access flags 33
+ * public class Hello {
+ *
+ * // compiled from: Hello.java
+ *
+ * // access flags 1
+ * public &lt;init&gt; ()V
+ * ALOAD 0
+ * INVOKESPECIAL java/lang/Object &lt;init&gt; ()V
+ * RETURN
+ * MAXSTACK = 1
+ * MAXLOCALS = 1
+ *
+ * // access flags 9
+ * public static main ([Ljava/lang/String;)V
+ * GETSTATIC java/lang/System out Ljava/io/PrintStream;
+ * LDC &quot;hello&quot;
+ * INVOKEVIRTUAL java/io/PrintStream println (Ljava/lang/String;)V
+ * RETURN
+ * MAXSTACK = 2
+ * MAXLOCALS = 1
+ * }
+ * </pre>
+ *
+ * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote>
+ *
+ * <pre>
+ * public class Hello {
+ *
+ * public static void main(String[] args) {
+ * System.out.println(&quot;hello&quot;);
+ * }
+ * }
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class TraceClassVisitor extends TraceAbstractVisitor implements
+ ClassVisitor
+{
+
+ /**
+ * The {@link ClassVisitor} to which this visitor delegates calls. May be
+ * <tt>null</tt>.
+ */
+ protected final ClassVisitor cv;
+
+ /**
+ * The print writer to be used to print the class.
+ */
+ protected final PrintWriter pw;
+
+ /**
+ * Prints a disassembled view of the given class to the standard output. <p>
+ * Usage: TraceClassVisitor [-debug] &lt;fully qualified class name or class
+ * file name &gt;
+ *
+ * @param args the command line arguments.
+ *
+ * @throws Exception if the class cannot be found, or if an IO exception
+ * occurs.
+ */
+ public static void main(final String[] args) throws Exception {
+ int i = 0;
+ int flags = ClassReader.SKIP_DEBUG;
+
+ boolean ok = true;
+ if (args.length < 1 || args.length > 2) {
+ ok = false;
+ }
+ if (ok && "-debug".equals(args[0])) {
+ i = 1;
+ flags = 0;
+ if (args.length != 2) {
+ ok = false;
+ }
+ }
+ if (!ok) {
+ System.err.println("Prints a disassembled view of the given class.");
+ System.err.println("Usage: TraceClassVisitor [-debug] "
+ + "<fully qualified class name or class file name>");
+ return;
+ }
+ ClassReader cr;
+ if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
+ || args[i].indexOf('/') > -1)
+ {
+ cr = new ClassReader(new FileInputStream(args[i]));
+ } else {
+ cr = new ClassReader(args[i]);
+ }
+ cr.accept(new TraceClassVisitor(new PrintWriter(System.out)),
+ getDefaultAttributes(),
+ flags);
+ }
+
+ /**
+ * Constructs a new {@link TraceClassVisitor}.
+ *
+ * @param pw the print writer to be used to print the class.
+ */
+ public TraceClassVisitor(final PrintWriter pw) {
+ this(null, pw);
+ }
+
+ /**
+ * Constructs a new {@link TraceClassVisitor}.
+ *
+ * @param cv the {@link ClassVisitor} to which this visitor delegates calls.
+ * May be <tt>null</tt>.
+ * @param pw the print writer to be used to print the class.
+ */
+ public TraceClassVisitor(final ClassVisitor cv, final PrintWriter pw) {
+ this.cv = cv;
+ this.pw = pw;
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the ClassVisitor interface
+ // ------------------------------------------------------------------------
+
+ public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces)
+ {
+ int major = version & 0xFFFF;
+ int minor = version >>> 16;
+ buf.setLength(0);
+ buf.append("// class version ")
+ .append(major)
+ .append('.')
+ .append(minor)
+ .append(" (")
+ .append(version)
+ .append(")\n");
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ buf.append("// DEPRECATED\n");
+ }
+ buf.append("// access flags ").append(access).append('\n');
+
+ appendDescriptor(CLASS_SIGNATURE, signature);
+ if (signature != null) {
+ TraceSignatureVisitor sv = new TraceSignatureVisitor(access);
+ SignatureReader r = new SignatureReader(signature);
+ r.accept(sv);
+ buf.append("// declaration: ")
+ .append(name)
+ .append(sv.getDeclaration())
+ .append('\n');
+ }
+
+ appendAccess(access & ~Opcodes.ACC_SUPER);
+ if ((access & Opcodes.ACC_ANNOTATION) != 0) {
+ buf.append("@interface ");
+ } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
+ buf.append("interface ");
+ } else if ((access & Opcodes.ACC_ENUM) == 0) {
+ buf.append("class ");
+ }
+ appendDescriptor(INTERNAL_NAME, name);
+
+ if (superName != null && !"java/lang/Object".equals(superName)) {
+ buf.append(" extends ");
+ appendDescriptor(INTERNAL_NAME, superName);
+ buf.append(' ');
+ }
+ if (interfaces != null && interfaces.length > 0) {
+ buf.append(" implements ");
+ for (int i = 0; i < interfaces.length; ++i) {
+ appendDescriptor(INTERNAL_NAME, interfaces[i]);
+ buf.append(' ');
+ }
+ }
+ buf.append(" {\n\n");
+
+ text.add(buf.toString());
+
+ if (cv != null) {
+ cv.visit(version, access, name, signature, superName, interfaces);
+ }
+ }
+
+ public void visitSource(final String file, final String debug) {
+ buf.setLength(0);
+ if (file != null) {
+ buf.append(tab)
+ .append("// compiled from: ")
+ .append(file)
+ .append('\n');
+ }
+ if (debug != null) {
+ buf.append(tab)
+ .append("// debug info: ")
+ .append(debug)
+ .append('\n');
+ }
+ if (buf.length() > 0) {
+ text.add(buf.toString());
+ }
+
+ if (cv != null) {
+ cv.visitSource(file, debug);
+ }
+ }
+
+ public void visitOuterClass(
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ buf.setLength(0);
+ buf.append(tab).append("OUTERCLASS ");
+ appendDescriptor(INTERNAL_NAME, owner);
+ buf.append(' ');
+ if (name != null) {
+ buf.append(name).append(' ');
+ }
+ appendDescriptor(METHOD_DESCRIPTOR, desc);
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (cv != null) {
+ cv.visitOuterClass(owner, name, desc);
+ }
+ }
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ text.add("\n");
+ AnnotationVisitor tav = super.visitAnnotation(desc, visible);
+ if (cv != null) {
+ ((TraceAnnotationVisitor) tav).av = cv.visitAnnotation(desc,
+ visible);
+ }
+ return tav;
+ }
+
+ public void visitAttribute(final Attribute attr) {
+ text.add("\n");
+ super.visitAttribute(attr);
+
+ if (cv != null) {
+ cv.visitAttribute(attr);
+ }
+ }
+
+ public void visitInnerClass(
+ final String name,
+ final String outerName,
+ final String innerName,
+ final int access)
+ {
+ buf.setLength(0);
+ buf.append(tab).append("// access flags ");
+ buf.append(access & ~Opcodes.ACC_SUPER).append('\n');
+ buf.append(tab);
+ appendAccess(access);
+ buf.append("INNERCLASS ");
+ appendDescriptor(INTERNAL_NAME, name);
+ buf.append(' ');
+ appendDescriptor(INTERNAL_NAME, outerName);
+ buf.append(' ');
+ appendDescriptor(INTERNAL_NAME, innerName);
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (cv != null) {
+ cv.visitInnerClass(name, outerName, innerName, access);
+ }
+ }
+
+ public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value)
+ {
+ buf.setLength(0);
+ buf.append('\n');
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ buf.append(tab).append("// DEPRECATED\n");
+ }
+ buf.append(tab).append("// access flags ").append(access).append('\n');
+ if (signature != null) {
+ buf.append(tab);
+ appendDescriptor(FIELD_SIGNATURE, signature);
+
+ TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
+ SignatureReader r = new SignatureReader(signature);
+ r.acceptType(sv);
+ buf.append(tab)
+ .append("// declaration: ")
+ .append(sv.getDeclaration())
+ .append('\n');
+ }
+
+ buf.append(tab);
+ appendAccess(access);
+
+ appendDescriptor(FIELD_DESCRIPTOR, desc);
+ buf.append(' ').append(name);
+ if (value != null) {
+ buf.append(" = ");
+ if (value instanceof String) {
+ buf.append('\"').append(value).append('\"');
+ } else {
+ buf.append(value);
+ }
+ }
+
+ buf.append('\n');
+ text.add(buf.toString());
+
+ TraceFieldVisitor tav = createTraceFieldVisitor();
+ text.add(tav.getText());
+
+ if (cv != null) {
+ tav.fv = cv.visitField(access, name, desc, signature, value);
+ }
+
+ return tav;
+ }
+
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions)
+ {
+ buf.setLength(0);
+ buf.append('\n');
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ buf.append(tab).append("// DEPRECATED\n");
+ }
+ buf.append(tab).append("// access flags ").append(access).append('\n');
+
+ if (signature != null) {
+ buf.append(tab);
+ appendDescriptor(METHOD_SIGNATURE, signature);
+
+ TraceSignatureVisitor v = new TraceSignatureVisitor(0);
+ SignatureReader r = new SignatureReader(signature);
+ r.accept(v);
+ String genericDecl = v.getDeclaration();
+ String genericReturn = v.getReturnType();
+ String genericExceptions = v.getExceptions();
+
+ buf.append(tab)
+ .append("// declaration: ")
+ .append(genericReturn)
+ .append(' ')
+ .append(name)
+ .append(genericDecl);
+ if (genericExceptions != null) {
+ buf.append(" throws ").append(genericExceptions);
+ }
+ buf.append('\n');
+ }
+
+ buf.append(tab);
+ appendAccess(access);
+ if ((access & Opcodes.ACC_NATIVE) != 0) {
+ buf.append("native ");
+ }
+ if ((access & Opcodes.ACC_VARARGS) != 0) {
+ buf.append("varargs ");
+ }
+ if ((access & Opcodes.ACC_BRIDGE) != 0) {
+ buf.append("bridge ");
+ }
+
+ buf.append(name);
+ appendDescriptor(METHOD_DESCRIPTOR, desc);
+ if (exceptions != null && exceptions.length > 0) {
+ buf.append(" throws ");
+ for (int i = 0; i < exceptions.length; ++i) {
+ appendDescriptor(INTERNAL_NAME, exceptions[i]);
+ buf.append(' ');
+ }
+ }
+
+ buf.append('\n');
+ text.add(buf.toString());
+
+ TraceMethodVisitor tcv = createTraceMethodVisitor();
+ text.add(tcv.getText());
+
+ if (cv != null) {
+ tcv.mv = cv.visitMethod(access, name, desc, signature, exceptions);
+ }
+
+ return tcv;
+ }
+
+ public void visitEnd() {
+ text.add("}\n");
+
+ print(pw);
+ pw.flush();
+
+ if (cv != null) {
+ cv.visitEnd();
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods
+ // ------------------------------------------------------------------------
+
+ protected TraceFieldVisitor createTraceFieldVisitor() {
+ return new TraceFieldVisitor();
+ }
+
+ protected TraceMethodVisitor createTraceMethodVisitor() {
+ return new TraceMethodVisitor();
+ }
+
+ /**
+ * Appends a string representation of the given access modifiers to {@link
+ * #buf buf}.
+ *
+ * @param access some access modifiers.
+ */
+ private void appendAccess(final int access) {
+ if ((access & Opcodes.ACC_PUBLIC) != 0) {
+ buf.append("public ");
+ }
+ if ((access & Opcodes.ACC_PRIVATE) != 0) {
+ buf.append("private ");
+ }
+ if ((access & Opcodes.ACC_PROTECTED) != 0) {
+ buf.append("protected ");
+ }
+ if ((access & Opcodes.ACC_FINAL) != 0) {
+ buf.append("final ");
+ }
+ if ((access & Opcodes.ACC_STATIC) != 0) {
+ buf.append("static ");
+ }
+ if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
+ buf.append("synchronized ");
+ }
+ if ((access & Opcodes.ACC_VOLATILE) != 0) {
+ buf.append("volatile ");
+ }
+ if ((access & Opcodes.ACC_TRANSIENT) != 0) {
+ buf.append("transient ");
+ }
+ if ((access & Opcodes.ACC_ABSTRACT) != 0) {
+ buf.append("abstract ");
+ }
+ if ((access & Opcodes.ACC_STRICT) != 0) {
+ buf.append("strictfp ");
+ }
+ if ((access & Opcodes.ACC_ENUM) != 0) {
+ buf.append("enum ");
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/TraceFieldVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/TraceFieldVisitor.java
new file mode 100644
index 0000000..a384350
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/TraceFieldVisitor.java
@@ -0,0 +1,78 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Attribute;
+import org.mockito.asm.FieldVisitor;
+
+/**
+ * A {@link FieldVisitor} that prints a disassembled view of the fields it
+ * visits.
+ *
+ * @author Eric Bruneton
+ */
+public class TraceFieldVisitor extends TraceAbstractVisitor implements
+ FieldVisitor
+{
+
+ /**
+ * The {@link FieldVisitor} to which this visitor delegates calls. May be
+ * <tt>null</tt>.
+ */
+ protected FieldVisitor fv;
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ AnnotationVisitor av = super.visitAnnotation(desc, visible);
+ if (fv != null) {
+ ((TraceAnnotationVisitor) av).av = fv.visitAnnotation(desc, visible);
+ }
+ return av;
+ }
+
+ public void visitAttribute(final Attribute attr) {
+ super.visitAttribute(attr);
+
+ if (fv != null) {
+ fv.visitAttribute(attr);
+ }
+ }
+
+ public void visitEnd() {
+ super.visitEnd();
+
+ if (fv != null) {
+ fv.visitEnd();
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/TraceMethodVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/TraceMethodVisitor.java
new file mode 100644
index 0000000..9b5faa6
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/TraceMethodVisitor.java
@@ -0,0 +1,567 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Attribute;
+import org.mockito.asm.Label;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+import org.mockito.asm.Type;
+import org.mockito.asm.signature.SignatureReader;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link MethodVisitor} that prints a disassembled view of the methods it
+ * visits.
+ *
+ * @author Eric Bruneton
+ */
+public class TraceMethodVisitor extends TraceAbstractVisitor implements
+ MethodVisitor
+{
+
+ /**
+ * The {@link MethodVisitor} to which this visitor delegates calls. May be
+ * <tt>null</tt>.
+ */
+ protected MethodVisitor mv;
+
+ /**
+ * Tab for bytecode instructions.
+ */
+ protected String tab2 = " ";
+
+ /**
+ * Tab for table and lookup switch instructions.
+ */
+ protected String tab3 = " ";
+
+ /**
+ * Tab for labels.
+ */
+ protected String ltab = " ";
+
+ /**
+ * The label names. This map associate String values to Label keys.
+ */
+ protected final Map labelNames;
+
+ /**
+ * Constructs a new {@link TraceMethodVisitor}.
+ */
+ public TraceMethodVisitor() {
+ this(null);
+ }
+
+ /**
+ * Constructs a new {@link TraceMethodVisitor}.
+ *
+ * @param mv the {@link MethodVisitor} to which this visitor delegates
+ * calls. May be <tt>null</tt>.
+ */
+ public TraceMethodVisitor(final MethodVisitor mv) {
+ this.labelNames = new HashMap();
+ this.mv = mv;
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the MethodVisitor interface
+ // ------------------------------------------------------------------------
+
+ public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible)
+ {
+ AnnotationVisitor av = super.visitAnnotation(desc, visible);
+ if (mv != null) {
+ ((TraceAnnotationVisitor) av).av = mv.visitAnnotation(desc, visible);
+ }
+ return av;
+ }
+
+ public void visitAttribute(final Attribute attr) {
+ buf.setLength(0);
+ buf.append(tab).append("ATTRIBUTE ");
+ appendDescriptor(-1, attr.type);
+
+ if (attr instanceof Traceable) {
+ ((Traceable) attr).trace(buf, labelNames);
+ } else {
+ buf.append(" : unknown\n");
+ }
+
+ text.add(buf.toString());
+ if (mv != null) {
+ mv.visitAttribute(attr);
+ }
+ }
+
+ public AnnotationVisitor visitAnnotationDefault() {
+ text.add(tab2 + "default=");
+ TraceAnnotationVisitor tav = createTraceAnnotationVisitor();
+ text.add(tav.getText());
+ text.add("\n");
+ if (mv != null) {
+ tav.av = mv.visitAnnotationDefault();
+ }
+ return tav;
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(
+ final int parameter,
+ final String desc,
+ final boolean visible)
+ {
+ buf.setLength(0);
+ buf.append(tab2).append('@');
+ appendDescriptor(FIELD_DESCRIPTOR, desc);
+ buf.append('(');
+ text.add(buf.toString());
+ TraceAnnotationVisitor tav = createTraceAnnotationVisitor();
+ text.add(tav.getText());
+ text.add(visible ? ") // parameter " : ") // invisible, parameter ");
+ text.add(new Integer(parameter));
+ text.add("\n");
+ if (mv != null) {
+ tav.av = mv.visitParameterAnnotation(parameter, desc, visible);
+ }
+ return tav;
+ }
+
+ public void visitCode() {
+ if (mv != null) {
+ mv.visitCode();
+ }
+ }
+
+ public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack)
+ {
+ buf.setLength(0);
+ buf.append(ltab);
+ buf.append("FRAME ");
+ switch (type) {
+ case Opcodes.F_NEW:
+ case Opcodes.F_FULL:
+ buf.append("FULL [");
+ appendFrameTypes(nLocal, local);
+ buf.append("] [");
+ appendFrameTypes(nStack, stack);
+ buf.append(']');
+ break;
+ case Opcodes.F_APPEND:
+ buf.append("APPEND [");
+ appendFrameTypes(nLocal, local);
+ buf.append(']');
+ break;
+ case Opcodes.F_CHOP:
+ buf.append("CHOP ").append(nLocal);
+ break;
+ case Opcodes.F_SAME:
+ buf.append("SAME");
+ break;
+ case Opcodes.F_SAME1:
+ buf.append("SAME1 ");
+ appendFrameTypes(1, stack);
+ break;
+ }
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitFrame(type, nLocal, local, nStack, stack);
+ }
+ }
+
+ public void visitInsn(final int opcode) {
+ buf.setLength(0);
+ buf.append(tab2).append(OPCODES[opcode]).append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitInsn(opcode);
+ }
+ }
+
+ public void visitIntInsn(final int opcode, final int operand) {
+ buf.setLength(0);
+ buf.append(tab2)
+ .append(OPCODES[opcode])
+ .append(' ')
+ .append(opcode == Opcodes.NEWARRAY
+ ? TYPES[operand]
+ : Integer.toString(operand))
+ .append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitIntInsn(opcode, operand);
+ }
+ }
+
+ public void visitVarInsn(final int opcode, final int var) {
+ buf.setLength(0);
+ buf.append(tab2)
+ .append(OPCODES[opcode])
+ .append(' ')
+ .append(var)
+ .append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitVarInsn(opcode, var);
+ }
+ }
+
+ public void visitTypeInsn(final int opcode, final String type) {
+ buf.setLength(0);
+ buf.append(tab2).append(OPCODES[opcode]).append(' ');
+ appendDescriptor(INTERNAL_NAME, type);
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitTypeInsn(opcode, type);
+ }
+ }
+
+ public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ buf.setLength(0);
+ buf.append(tab2).append(OPCODES[opcode]).append(' ');
+ appendDescriptor(INTERNAL_NAME, owner);
+ buf.append('.').append(name).append(" : ");
+ appendDescriptor(FIELD_DESCRIPTOR, desc);
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitFieldInsn(opcode, owner, name, desc);
+ }
+ }
+
+ public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ buf.setLength(0);
+ buf.append(tab2).append(OPCODES[opcode]).append(' ');
+ appendDescriptor(INTERNAL_NAME, owner);
+ buf.append('.').append(name).append(' ');
+ appendDescriptor(METHOD_DESCRIPTOR, desc);
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitMethodInsn(opcode, owner, name, desc);
+ }
+ }
+
+ public void visitJumpInsn(final int opcode, final Label label) {
+ buf.setLength(0);
+ buf.append(tab2).append(OPCODES[opcode]).append(' ');
+ appendLabel(label);
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitJumpInsn(opcode, label);
+ }
+ }
+
+ public void visitLabel(final Label label) {
+ buf.setLength(0);
+ buf.append(ltab);
+ appendLabel(label);
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitLabel(label);
+ }
+ }
+
+ public void visitLdcInsn(final Object cst) {
+ buf.setLength(0);
+ buf.append(tab2).append("LDC ");
+ if (cst instanceof String) {
+ AbstractVisitor.appendString(buf, (String) cst);
+ } else if (cst instanceof Type) {
+ buf.append(((Type) cst).getDescriptor()).append(".class");
+ } else {
+ buf.append(cst);
+ }
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitLdcInsn(cst);
+ }
+ }
+
+ public void visitIincInsn(final int var, final int increment) {
+ buf.setLength(0);
+ buf.append(tab2)
+ .append("IINC ")
+ .append(var)
+ .append(' ')
+ .append(increment)
+ .append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitIincInsn(var, increment);
+ }
+ }
+
+ public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label[] labels)
+ {
+ buf.setLength(0);
+ buf.append(tab2).append("TABLESWITCH\n");
+ for (int i = 0; i < labels.length; ++i) {
+ buf.append(tab3).append(min + i).append(": ");
+ appendLabel(labels[i]);
+ buf.append('\n');
+ }
+ buf.append(tab3).append("default: ");
+ appendLabel(dflt);
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+ }
+
+ public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int[] keys,
+ final Label[] labels)
+ {
+ buf.setLength(0);
+ buf.append(tab2).append("LOOKUPSWITCH\n");
+ for (int i = 0; i < labels.length; ++i) {
+ buf.append(tab3).append(keys[i]).append(": ");
+ appendLabel(labels[i]);
+ buf.append('\n');
+ }
+ buf.append(tab3).append("default: ");
+ appendLabel(dflt);
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitLookupSwitchInsn(dflt, keys, labels);
+ }
+ }
+
+ public void visitMultiANewArrayInsn(final String desc, final int dims) {
+ buf.setLength(0);
+ buf.append(tab2).append("MULTIANEWARRAY ");
+ appendDescriptor(FIELD_DESCRIPTOR, desc);
+ buf.append(' ').append(dims).append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitMultiANewArrayInsn(desc, dims);
+ }
+ }
+
+ public void visitTryCatchBlock(
+ final Label start,
+ final Label end,
+ final Label handler,
+ final String type)
+ {
+ buf.setLength(0);
+ buf.append(tab2).append("TRYCATCHBLOCK ");
+ appendLabel(start);
+ buf.append(' ');
+ appendLabel(end);
+ buf.append(' ');
+ appendLabel(handler);
+ buf.append(' ');
+ appendDescriptor(INTERNAL_NAME, type);
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitTryCatchBlock(start, end, handler, type);
+ }
+ }
+
+ public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index)
+ {
+ buf.setLength(0);
+ buf.append(tab2).append("LOCALVARIABLE ").append(name).append(' ');
+ appendDescriptor(FIELD_DESCRIPTOR, desc);
+ buf.append(' ');
+ appendLabel(start);
+ buf.append(' ');
+ appendLabel(end);
+ buf.append(' ').append(index).append('\n');
+
+ if (signature != null) {
+ buf.append(tab2);
+ appendDescriptor(FIELD_SIGNATURE, signature);
+
+ TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
+ SignatureReader r = new SignatureReader(signature);
+ r.acceptType(sv);
+ buf.append(tab2)
+ .append("// declaration: ")
+ .append(sv.getDeclaration())
+ .append('\n');
+ }
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitLocalVariable(name, desc, signature, start, end, index);
+ }
+ }
+
+ public void visitLineNumber(final int line, final Label start) {
+ buf.setLength(0);
+ buf.append(tab2).append("LINENUMBER ").append(line).append(' ');
+ appendLabel(start);
+ buf.append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitLineNumber(line, start);
+ }
+ }
+
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ buf.setLength(0);
+ buf.append(tab2).append("MAXSTACK = ").append(maxStack).append('\n');
+ text.add(buf.toString());
+
+ buf.setLength(0);
+ buf.append(tab2).append("MAXLOCALS = ").append(maxLocals).append('\n');
+ text.add(buf.toString());
+
+ if (mv != null) {
+ mv.visitMaxs(maxStack, maxLocals);
+ }
+ }
+
+ public void visitEnd() {
+ super.visitEnd();
+
+ if (mv != null) {
+ mv.visitEnd();
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods
+ // ------------------------------------------------------------------------
+
+ private void appendFrameTypes(final int n, final Object[] o) {
+ for (int i = 0; i < n; ++i) {
+ if (i > 0) {
+ buf.append(' ');
+ }
+ if (o[i] instanceof String) {
+ String desc = (String) o[i];
+ if (desc.startsWith("[")) {
+ appendDescriptor(FIELD_DESCRIPTOR, desc);
+ } else {
+ appendDescriptor(INTERNAL_NAME, desc);
+ }
+ } else if (o[i] instanceof Integer) {
+ switch (((Integer) o[i]).intValue()) {
+ case 0:
+ appendDescriptor(FIELD_DESCRIPTOR, "T");
+ break;
+ case 1:
+ appendDescriptor(FIELD_DESCRIPTOR, "I");
+ break;
+ case 2:
+ appendDescriptor(FIELD_DESCRIPTOR, "F");
+ break;
+ case 3:
+ appendDescriptor(FIELD_DESCRIPTOR, "D");
+ break;
+ case 4:
+ appendDescriptor(FIELD_DESCRIPTOR, "J");
+ break;
+ case 5:
+ appendDescriptor(FIELD_DESCRIPTOR, "N");
+ break;
+ case 6:
+ appendDescriptor(FIELD_DESCRIPTOR, "U");
+ break;
+ }
+ } else {
+ appendLabel((Label) o[i]);
+ }
+ }
+ }
+
+ /**
+ * Appends the name of the given label to {@link #buf buf}. Creates a new
+ * label name if the given label does not yet have one.
+ *
+ * @param l a label.
+ */
+ protected void appendLabel(final Label l) {
+ String name = (String) labelNames.get(l);
+ if (name == null) {
+ name = "L" + labelNames.size();
+ labelNames.put(l, name);
+ }
+ buf.append(name);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/TraceSignatureVisitor.java b/cglib-and-asm/src/org/mockito/asm/util/TraceSignatureVisitor.java
new file mode 100644
index 0000000..4006a51
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/TraceSignatureVisitor.java
@@ -0,0 +1,300 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import org.mockito.asm.Opcodes;
+import org.mockito.asm.signature.SignatureVisitor;
+
+/**
+ * A {@link SignatureVisitor} that prints a disassembled view of the signature
+ * it visits.
+ *
+ * @author Eugene Kuleshov
+ * @author Eric Bruneton
+ */
+public class TraceSignatureVisitor implements SignatureVisitor {
+
+ private final StringBuffer declaration;
+
+ private boolean isInterface;
+
+ private boolean seenFormalParameter;
+
+ private boolean seenInterfaceBound;
+
+ private boolean seenParameter;
+
+ private boolean seenInterface;
+
+ private StringBuffer returnType;
+
+ private StringBuffer exceptions;
+
+ /**
+ * Stack used to keep track of class types that have arguments. Each element
+ * of this stack is a boolean encoded in one bit. The top of the stack is
+ * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
+ * /2.
+ */
+ private int argumentStack;
+
+ /**
+ * Stack used to keep track of array class types. Each element of this stack
+ * is a boolean encoded in one bit. The top of the stack is the lowest order
+ * bit. Pushing false = *2, pushing true = *2+1, popping = /2.
+ */
+ private int arrayStack;
+
+ private String separator = "";
+
+ public TraceSignatureVisitor(final int access) {
+ isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
+ this.declaration = new StringBuffer();
+ }
+
+ private TraceSignatureVisitor(final StringBuffer buf) {
+ this.declaration = buf;
+ }
+
+ public void visitFormalTypeParameter(final String name) {
+ declaration.append(seenFormalParameter ? ", " : "<").append(name);
+ seenFormalParameter = true;
+ seenInterfaceBound = false;
+ }
+
+ public SignatureVisitor visitClassBound() {
+ separator = " extends ";
+ startType();
+ return this;
+ }
+
+ public SignatureVisitor visitInterfaceBound() {
+ separator = seenInterfaceBound ? ", " : " extends ";
+ seenInterfaceBound = true;
+ startType();
+ return this;
+ }
+
+ public SignatureVisitor visitSuperclass() {
+ endFormals();
+ separator = " extends ";
+ startType();
+ return this;
+ }
+
+ public SignatureVisitor visitInterface() {
+ separator = seenInterface ? ", " : isInterface
+ ? " extends "
+ : " implements ";
+ seenInterface = true;
+ startType();
+ return this;
+ }
+
+ public SignatureVisitor visitParameterType() {
+ endFormals();
+ if (seenParameter) {
+ declaration.append(", ");
+ } else {
+ seenParameter = true;
+ declaration.append('(');
+ }
+ startType();
+ return this;
+ }
+
+ public SignatureVisitor visitReturnType() {
+ endFormals();
+ if (seenParameter) {
+ seenParameter = false;
+ } else {
+ declaration.append('(');
+ }
+ declaration.append(')');
+ returnType = new StringBuffer();
+ return new TraceSignatureVisitor(returnType);
+ }
+
+ public SignatureVisitor visitExceptionType() {
+ if (exceptions == null) {
+ exceptions = new StringBuffer();
+ } else {
+ exceptions.append(", ");
+ }
+ // startType();
+ return new TraceSignatureVisitor(exceptions);
+ }
+
+ public void visitBaseType(final char descriptor) {
+ switch (descriptor) {
+ case 'V':
+ declaration.append("void");
+ break;
+ case 'B':
+ declaration.append("byte");
+ break;
+ case 'J':
+ declaration.append("long");
+ break;
+ case 'Z':
+ declaration.append("boolean");
+ break;
+ case 'I':
+ declaration.append("int");
+ break;
+ case 'S':
+ declaration.append("short");
+ break;
+ case 'C':
+ declaration.append("char");
+ break;
+ case 'F':
+ declaration.append("float");
+ break;
+ // case 'D':
+ default:
+ declaration.append("double");
+ break;
+ }
+ endType();
+ }
+
+ public void visitTypeVariable(final String name) {
+ declaration.append(name);
+ endType();
+ }
+
+ public SignatureVisitor visitArrayType() {
+ startType();
+ arrayStack |= 1;
+ return this;
+ }
+
+ public void visitClassType(final String name) {
+ if ("java/lang/Object".equals(name)) {
+ // Map<java.lang.Object,java.util.List>
+ // or
+ // abstract public V get(Object key); (seen in Dictionary.class)
+ // should have Object
+ // but java.lang.String extends java.lang.Object is unnecessary
+ boolean needObjectClass = argumentStack % 2 != 0 || seenParameter;
+ if (needObjectClass) {
+ declaration.append(separator).append(name.replace('/', '.'));
+ }
+ } else {
+ declaration.append(separator).append(name.replace('/', '.'));
+ }
+ separator = "";
+ argumentStack *= 2;
+ }
+
+ public void visitInnerClassType(final String name) {
+ if (argumentStack % 2 != 0) {
+ declaration.append('>');
+ }
+ argumentStack /= 2;
+ declaration.append('.');
+ declaration.append(separator).append(name.replace('/', '.'));
+ separator = "";
+ argumentStack *= 2;
+ }
+
+ public void visitTypeArgument() {
+ if (argumentStack % 2 == 0) {
+ ++argumentStack;
+ declaration.append('<');
+ } else {
+ declaration.append(", ");
+ }
+ declaration.append('?');
+ }
+
+ public SignatureVisitor visitTypeArgument(final char tag) {
+ if (argumentStack % 2 == 0) {
+ ++argumentStack;
+ declaration.append('<');
+ } else {
+ declaration.append(", ");
+ }
+
+ if (tag == EXTENDS) {
+ declaration.append("? extends ");
+ } else if (tag == SUPER) {
+ declaration.append("? super ");
+ }
+
+ startType();
+ return this;
+ }
+
+ public void visitEnd() {
+ if (argumentStack % 2 != 0) {
+ declaration.append('>');
+ }
+ argumentStack /= 2;
+ endType();
+ }
+
+ public String getDeclaration() {
+ return declaration.toString();
+ }
+
+ public String getReturnType() {
+ return returnType == null ? null : returnType.toString();
+ }
+
+ public String getExceptions() {
+ return exceptions == null ? null : exceptions.toString();
+ }
+
+ // -----------------------------------------------
+
+ private void endFormals() {
+ if (seenFormalParameter) {
+ declaration.append('>');
+ seenFormalParameter = false;
+ }
+ }
+
+ private void startType() {
+ arrayStack *= 2;
+ }
+
+ private void endType() {
+ if (arrayStack % 2 == 0) {
+ arrayStack /= 2;
+ } else {
+ while (arrayStack % 2 != 0) {
+ arrayStack /= 2;
+ declaration.append("[]");
+ }
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/Traceable.java b/cglib-and-asm/src/org/mockito/asm/util/Traceable.java
new file mode 100644
index 0000000..8a73467
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/Traceable.java
@@ -0,0 +1,52 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.asm.util;
+
+import java.util.Map;
+
+/**
+ * An attribute that can print eadable representation of the attribute.
+ *
+ * Implementation should construct readable output from an attribute data
+ * structures for current attribute state. Such representation could be used in
+ * unit test assertions.
+ *
+ * @author Eugene Kuleshov
+ */
+public interface Traceable {
+
+ /**
+ * Build a human readable representation of the attribute.
+ *
+ * @param buf A buffer used for printing Java code.
+ * @param labelNames map of label instances to their names.
+ */
+ void trace(StringBuffer buf, Map labelNames);
+}
diff --git a/cglib-and-asm/src/org/mockito/asm/util/package.html b/cglib-and-asm/src/org/mockito/asm/util/package.html
new file mode 100644
index 0000000..ce91531
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/asm/util/package.html
@@ -0,0 +1,40 @@
+<html>
+<!--
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<body>
+Provides ASM visitors that can be useful for programming and
+debugging purposes. These class visitors are normally not used by applications
+at runtime. This is why they are bundled in an optional <tt>asm-util.jar</tt>
+library that is separated from (but requires) the <tt>asm.jar</tt> library,
+which contains the core ASM framework.
+
+@since ASM 1.3.2
+</body>
+</html>
diff --git a/cglib-and-asm/src/org/mockito/cglib/beans/BeanCopier.java b/cglib-and-asm/src/org/mockito/cglib/beans/BeanCopier.java
new file mode 100644
index 0000000..14fe383
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/beans/BeanCopier.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.beans;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+import java.util.*;
+
+/**
+ * @author Chris Nokleberg
+ */
+abstract public class BeanCopier
+{
+ private static final BeanCopierKey KEY_FACTORY =
+ (BeanCopierKey)KeyFactory.create(BeanCopierKey.class);
+ private static final Type CONVERTER =
+ TypeUtils.parseType("org.mockito.cglib.core.Converter");
+ private static final Type BEAN_COPIER =
+ TypeUtils.parseType("org.mockito.cglib.beans.BeanCopier");
+ private static final Signature COPY =
+ new Signature("copy", Type.VOID_TYPE, new Type[]{ Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER });
+ private static final Signature CONVERT =
+ TypeUtils.parseSignature("Object convert(Object, Class, Object)");
+
+ interface BeanCopierKey {
+ public Object newInstance(String source, String target, boolean useConverter);
+ }
+
+ public static BeanCopier create(Class source, Class target, boolean useConverter) {
+ Generator gen = new Generator();
+ gen.setSource(source);
+ gen.setTarget(target);
+ gen.setUseConverter(useConverter);
+ return gen.create();
+ }
+
+ abstract public void copy(Object from, Object to, Converter converter);
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(BeanCopier.class.getName());
+ private Class source;
+ private Class target;
+ private boolean useConverter;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ public void setSource(Class source) {
+ if(!Modifier.isPublic(source.getModifiers())){
+ setNamePrefix(source.getName());
+ }
+ this.source = source;
+ }
+
+ public void setTarget(Class target) {
+ if(!Modifier.isPublic(target.getModifiers())){
+ setNamePrefix(target.getName());
+ }
+
+ this.target = target;
+ }
+
+ public void setUseConverter(boolean useConverter) {
+ this.useConverter = useConverter;
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return source.getClassLoader();
+ }
+
+ public BeanCopier create() {
+ Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
+ return (BeanCopier)super.create(key);
+ }
+
+ public void generateClass(ClassVisitor v) {
+ Type sourceType = Type.getType(source);
+ Type targetType = Type.getType(target);
+ ClassEmitter ce = new ClassEmitter(v);
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ BEAN_COPIER,
+ null,
+ Constants.SOURCE_FILE);
+
+ EmitUtils.null_constructor(ce);
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
+ PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
+ PropertyDescriptor[] setters = ReflectUtils.getBeanGetters(target);
+
+ Map names = new HashMap();
+ for (int i = 0; i < getters.length; i++) {
+ names.put(getters[i].getName(), getters[i]);
+ }
+ Local targetLocal = e.make_local();
+ Local sourceLocal = e.make_local();
+ if (useConverter) {
+ e.load_arg(1);
+ e.checkcast(targetType);
+ e.store_local(targetLocal);
+ e.load_arg(0);
+ e.checkcast(sourceType);
+ e.store_local(sourceLocal);
+ } else {
+ e.load_arg(1);
+ e.checkcast(targetType);
+ e.load_arg(0);
+ e.checkcast(sourceType);
+ }
+ for (int i = 0; i < setters.length; i++) {
+ PropertyDescriptor setter = setters[i];
+ PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());
+ if (getter != null) {
+ MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
+ MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
+ if (useConverter) {
+ Type setterType = write.getSignature().getArgumentTypes()[0];
+ e.load_local(targetLocal);
+ e.load_arg(2);
+ e.load_local(sourceLocal);
+ e.invoke(read);
+ e.box(read.getSignature().getReturnType());
+ EmitUtils.load_class(e, setterType);
+ e.push(write.getSignature().getName());
+ e.invoke_interface(CONVERTER, CONVERT);
+ e.unbox_or_zero(setterType);
+ e.invoke(write);
+ } else if (compatible(getter, setter)) {
+ e.dup2();
+ e.invoke(read);
+ e.invoke(write);
+ }
+ }
+ }
+ e.return_value();
+ e.end_method();
+ ce.end_class();
+ }
+
+ private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
+ // TODO: allow automatic widening conversions?
+ return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
+ }
+
+ protected Object firstInstance(Class type) {
+ return ReflectUtils.newInstance(type);
+ }
+
+ protected Object nextInstance(Object instance) {
+ return instance;
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/beans/BeanGenerator.java b/cglib-and-asm/src/org/mockito/cglib/beans/BeanGenerator.java
new file mode 100644
index 0000000..63c3ddd
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/beans/BeanGenerator.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+/**
+ * @author Juozas Baliuka, Chris Nokleberg
+ */
+public class BeanGenerator extends AbstractClassGenerator
+{
+ private static final Source SOURCE = new Source(BeanGenerator.class.getName());
+ private static final BeanGeneratorKey KEY_FACTORY =
+ (BeanGeneratorKey)KeyFactory.create(BeanGeneratorKey.class);
+
+ interface BeanGeneratorKey {
+ public Object newInstance(String superclass, Map props);
+ }
+
+ private Class superclass;
+ private Map props = new HashMap();
+ private boolean classOnly;
+
+ public BeanGenerator() {
+ super(SOURCE);
+ }
+
+ /**
+ * Set the class which the generated class will extend. The class
+ * must not be declared as final, and must have a non-private
+ * no-argument constructor.
+ * @param superclass class to extend, or null to extend Object
+ */
+ public void setSuperclass(Class superclass) {
+ if (superclass != null && superclass.equals(Object.class)) {
+ superclass = null;
+ }
+ this.superclass = superclass;
+ }
+
+ public void addProperty(String name, Class type) {
+ if (props.containsKey(name)) {
+ throw new IllegalArgumentException("Duplicate property name \"" + name + "\"");
+ }
+ props.put(name, Type.getType(type));
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ if (superclass != null) {
+ return superclass.getClassLoader();
+ } else {
+ return null;
+ }
+ }
+
+ public Object create() {
+ classOnly = false;
+ return createHelper();
+ }
+
+ public Object createClass() {
+ classOnly = true;
+ return createHelper();
+ }
+
+ private Object createHelper() {
+ if (superclass != null) {
+ setNamePrefix(superclass.getName());
+ }
+ String superName = (superclass != null) ? superclass.getName() : "java.lang.Object";
+ Object key = KEY_FACTORY.newInstance(superName, props);
+ return super.create(key);
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ int size = props.size();
+ String[] names = (String[])props.keySet().toArray(new String[size]);
+ Type[] types = new Type[size];
+ for (int i = 0; i < size; i++) {
+ types[i] = (Type)props.get(names[i]);
+ }
+ ClassEmitter ce = new ClassEmitter(v);
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ superclass != null ? Type.getType(superclass) : Constants.TYPE_OBJECT,
+ null,
+ null);
+ EmitUtils.null_constructor(ce);
+ EmitUtils.add_properties(ce, names, types);
+ ce.end_class();
+ }
+
+ protected Object firstInstance(Class type) {
+ if (classOnly) {
+ return type;
+ } else {
+ return ReflectUtils.newInstance(type);
+ }
+ }
+
+ protected Object nextInstance(Object instance) {
+ Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass();
+ if (classOnly) {
+ return protoclass;
+ } else {
+ return ReflectUtils.newInstance(protoclass);
+ }
+ }
+
+ public static void addProperties(BeanGenerator gen, Map props) {
+ for (Iterator it = props.keySet().iterator(); it.hasNext();) {
+ String name = (String)it.next();
+ gen.addProperty(name, (Class)props.get(name));
+ }
+ }
+
+ public static void addProperties(BeanGenerator gen, Class type) {
+ addProperties(gen, ReflectUtils.getBeanProperties(type));
+ }
+
+ public static void addProperties(BeanGenerator gen, PropertyDescriptor[] descriptors) {
+ for (int i = 0; i < descriptors.length; i++) {
+ gen.addProperty(descriptors[i].getName(), descriptors[i].getPropertyType());
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/beans/BeanMap.java b/cglib-and-asm/src/org/mockito/cglib/beans/BeanMap.java
new file mode 100644
index 0000000..78d06f8
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/beans/BeanMap.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.beans;
+
+import java.beans.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.cglib.core.*;
+
+/**
+ * A <code>Map</code>-based view of a JavaBean. The default set of keys is the
+ * union of all property names (getters or setters). An attempt to set
+ * a read-only property will be ignored, and write-only properties will
+ * be returned as <code>null</code>. Removal of objects is not a
+ * supported (the key set is fixed).
+ * @author Chris Nokleberg
+ */
+abstract public class BeanMap implements Map {
+ /**
+ * Limit the properties reflected in the key set of the map
+ * to readable properties.
+ * @see BeanMap.Generator#setRequire
+ */
+ public static final int REQUIRE_GETTER = 1;
+
+ /**
+ * Limit the properties reflected in the key set of the map
+ * to writable properties.
+ * @see BeanMap.Generator#setRequire
+ */
+ public static final int REQUIRE_SETTER = 2;
+
+ /**
+ * Helper method to create a new <code>BeanMap</code>. For finer
+ * control over the generated instance, use a new instance of
+ * <code>BeanMap.Generator</code> instead of this static method.
+ * @param bean the JavaBean underlying the map
+ * @return a new <code>BeanMap</code> instance
+ */
+ public static BeanMap create(Object bean) {
+ Generator gen = new Generator();
+ gen.setBean(bean);
+ return gen.create();
+ }
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(BeanMap.class.getName());
+
+ private static final BeanMapKey KEY_FACTORY =
+ (BeanMapKey)KeyFactory.create(BeanMapKey.class, KeyFactory.CLASS_BY_NAME);
+
+ interface BeanMapKey {
+ public Object newInstance(Class type, int require);
+ }
+
+ private Object bean;
+ private Class beanClass;
+ private int require;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ /**
+ * Set the bean that the generated map should reflect. The bean may be swapped
+ * out for another bean of the same type using {@link #setBean}.
+ * Calling this method overrides any value previously set using {@link #setBeanClass}.
+ * You must call either this method or {@link #setBeanClass} before {@link #create}.
+ * @param bean the initial bean
+ */
+ public void setBean(Object bean) {
+ this.bean = bean;
+ if (bean != null)
+ beanClass = bean.getClass();
+ }
+
+ /**
+ * Set the class of the bean that the generated map should support.
+ * You must call either this method or {@link #setBeanClass} before {@link #create}.
+ * @param beanClass the class of the bean
+ */
+ public void setBeanClass(Class beanClass) {
+ this.beanClass = beanClass;
+ }
+
+ /**
+ * Limit the properties reflected by the generated map.
+ * @param require any combination of {@link #REQUIRE_GETTER} and
+ * {@link #REQUIRE_SETTER}; default is zero (any property allowed)
+ */
+ public void setRequire(int require) {
+ this.require = require;
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return beanClass.getClassLoader();
+ }
+
+ /**
+ * Create a new instance of the <code>BeanMap</code>. An existing
+ * generated class will be reused if possible.
+ */
+ public BeanMap create() {
+ if (beanClass == null)
+ throw new IllegalArgumentException("Class of bean unknown");
+ setNamePrefix(beanClass.getName());
+ return (BeanMap)super.create(KEY_FACTORY.newInstance(beanClass, require));
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ new BeanMapEmitter(v, getClassName(), beanClass, require);
+ }
+
+ protected Object firstInstance(Class type) {
+ return ((BeanMap)ReflectUtils.newInstance(type)).newInstance(bean);
+ }
+
+ protected Object nextInstance(Object instance) {
+ return ((BeanMap)instance).newInstance(bean);
+ }
+ }
+
+ /**
+ * Create a new <code>BeanMap</code> instance using the specified bean.
+ * This is faster than using the {@link #create} static method.
+ * @param bean the JavaBean underlying the map
+ * @return a new <code>BeanMap</code> instance
+ */
+ abstract public BeanMap newInstance(Object bean);
+
+ /**
+ * Get the type of a property.
+ * @param name the name of the JavaBean property
+ * @return the type of the property, or null if the property does not exist
+ */
+ abstract public Class getPropertyType(String name);
+
+ protected Object bean;
+
+ protected BeanMap() {
+ }
+
+ protected BeanMap(Object bean) {
+ setBean(bean);
+ }
+
+ public Object get(Object key) {
+ return get(bean, key);
+ }
+
+ public Object put(Object key, Object value) {
+ return put(bean, key, value);
+ }
+
+ /**
+ * Get the property of a bean. This allows a <code>BeanMap</code>
+ * to be used statically for multiple beans--the bean instance tied to the
+ * map is ignored and the bean passed to this method is used instead.
+ * @param bean the bean to query; must be compatible with the type of
+ * this <code>BeanMap</code>
+ * @param key must be a String
+ * @return the current value, or null if there is no matching property
+ */
+ abstract public Object get(Object bean, Object key);
+
+ /**
+ * Set the property of a bean. This allows a <code>BeanMap</code>
+ * to be used statically for multiple beans--the bean instance tied to the
+ * map is ignored and the bean passed to this method is used instead.
+ * @param key must be a String
+ * @return the old value, if there was one, or null
+ */
+ abstract public Object put(Object bean, Object key, Object value);
+
+ /**
+ * Change the underlying bean this map should use.
+ * @param bean the new JavaBean
+ * @see #getBean
+ */
+ public void setBean(Object bean) {
+ this.bean = bean;
+ }
+
+ /**
+ * Return the bean currently in use by this map.
+ * @return the current JavaBean
+ * @see #setBean
+ */
+ public Object getBean() {
+ return bean;
+ }
+
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean containsKey(Object key) {
+ return keySet().contains(key);
+ }
+
+ public boolean containsValue(Object value) {
+ for (Iterator it = keySet().iterator(); it.hasNext();) {
+ Object v = get(it.next());
+ if (((value == null) && (v == null)) || value.equals(v))
+ return true;
+ }
+ return false;
+ }
+
+ public int size() {
+ return keySet().size();
+ }
+
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ public Object remove(Object key) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void putAll(Map t) {
+ for (Iterator it = t.keySet().iterator(); it.hasNext();) {
+ Object key = it.next();
+ put(key, t.get(key));
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof Map)) {
+ return false;
+ }
+ Map other = (Map)o;
+ if (size() != other.size()) {
+ return false;
+ }
+ for (Iterator it = keySet().iterator(); it.hasNext();) {
+ Object key = it.next();
+ if (!other.containsKey(key)) {
+ return false;
+ }
+ Object v1 = get(key);
+ Object v2 = other.get(key);
+ if (!((v1 == null) ? v2 == null : v1.equals(v2))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int hashCode() {
+ int code = 0;
+ for (Iterator it = keySet().iterator(); it.hasNext();) {
+ Object key = it.next();
+ Object value = get(key);
+ code += ((key == null) ? 0 : key.hashCode()) ^
+ ((value == null) ? 0 : value.hashCode());
+ }
+ return code;
+ }
+
+ // TODO: optimize
+ public Set entrySet() {
+ HashMap copy = new HashMap();
+ for (Iterator it = keySet().iterator(); it.hasNext();) {
+ Object key = it.next();
+ copy.put(key, get(key));
+ }
+ return Collections.unmodifiableMap(copy).entrySet();
+ }
+
+ public Collection values() {
+ Set keys = keySet();
+ List values = new ArrayList(keys.size());
+ for (Iterator it = keys.iterator(); it.hasNext();) {
+ values.add(get(it.next()));
+ }
+ return Collections.unmodifiableCollection(values);
+ }
+
+ /*
+ * @see java.util.AbstractMap#toString
+ */
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append('{');
+ for (Iterator it = keySet().iterator(); it.hasNext();) {
+ Object key = it.next();
+ sb.append(key);
+ sb.append('=');
+ sb.append(get(key));
+ if (it.hasNext()) {
+ sb.append(", ");
+ }
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/beans/BeanMapEmitter.java b/cglib-and-asm/src/org/mockito/cglib/beans/BeanMapEmitter.java
new file mode 100644
index 0000000..4ced61c
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/beans/BeanMapEmitter.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.beans;
+
+import java.beans.*;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Label;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+class BeanMapEmitter extends ClassEmitter {
+ private static final Type BEAN_MAP =
+ TypeUtils.parseType("org.mockito.cglib.beans.BeanMap");
+ private static final Type FIXED_KEY_SET =
+ TypeUtils.parseType("org.mockito.cglib.beans.FixedKeySet");
+ private static final Signature CSTRUCT_OBJECT =
+ TypeUtils.parseConstructor("Object");
+ private static final Signature CSTRUCT_STRING_ARRAY =
+ TypeUtils.parseConstructor("String[]");
+ private static final Signature BEAN_MAP_GET =
+ TypeUtils.parseSignature("Object get(Object, Object)");
+ private static final Signature BEAN_MAP_PUT =
+ TypeUtils.parseSignature("Object put(Object, Object, Object)");
+ private static final Signature KEY_SET =
+ TypeUtils.parseSignature("java.util.Set keySet()");
+ private static final Signature NEW_INSTANCE =
+ new Signature("newInstance", BEAN_MAP, new Type[]{ Constants.TYPE_OBJECT });
+ private static final Signature GET_PROPERTY_TYPE =
+ TypeUtils.parseSignature("Class getPropertyType(String)");
+
+ public BeanMapEmitter(ClassVisitor v, String className, Class type, int require) {
+ super(v);
+
+ begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, BEAN_MAP, null, Constants.SOURCE_FILE);
+ EmitUtils.null_constructor(this);
+ EmitUtils.factory_method(this, NEW_INSTANCE);
+ generateConstructor();
+
+ Map getters = makePropertyMap(ReflectUtils.getBeanGetters(type));
+ Map setters = makePropertyMap(ReflectUtils.getBeanSetters(type));
+ Map allProps = new HashMap();
+ allProps.putAll(getters);
+ allProps.putAll(setters);
+
+ if (require != 0) {
+ for (Iterator it = allProps.keySet().iterator(); it.hasNext();) {
+ String name = (String)it.next();
+ if ((((require & BeanMap.REQUIRE_GETTER) != 0) && !getters.containsKey(name)) ||
+ (((require & BeanMap.REQUIRE_SETTER) != 0) && !setters.containsKey(name))) {
+ it.remove();
+ getters.remove(name);
+ setters.remove(name);
+ }
+ }
+ }
+ generateGet(type, getters);
+ generatePut(type, setters);
+
+ String[] allNames = getNames(allProps);
+ generateKeySet(allNames);
+ generateGetPropertyType(allProps, allNames);
+ end_class();
+ }
+
+ private Map makePropertyMap(PropertyDescriptor[] props) {
+ Map names = new HashMap();
+ for (int i = 0; i < props.length; i++) {
+ names.put(((PropertyDescriptor)props[i]).getName(), props[i]);
+ }
+ return names;
+ }
+
+ private String[] getNames(Map propertyMap) {
+ return (String[])propertyMap.keySet().toArray(new String[propertyMap.size()]);
+ }
+
+ private void generateConstructor() {
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null);
+ e.load_this();
+ e.load_arg(0);
+ e.super_invoke_constructor(CSTRUCT_OBJECT);
+ e.return_value();
+ e.end_method();
+ }
+
+ private void generateGet(Class type, final Map getters) {
+ final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_GET, null);
+ e.load_arg(0);
+ e.checkcast(Type.getType(type));
+ e.load_arg(1);
+ e.checkcast(Constants.TYPE_STRING);
+ EmitUtils.string_switch(e, getNames(getters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ PropertyDescriptor pd = (PropertyDescriptor)getters.get(key);
+ MethodInfo method = ReflectUtils.getMethodInfo(pd.getReadMethod());
+ e.invoke(method);
+ e.box(method.getSignature().getReturnType());
+ e.return_value();
+ }
+ public void processDefault() {
+ e.aconst_null();
+ e.return_value();
+ }
+ });
+ e.end_method();
+ }
+
+ private void generatePut(Class type, final Map setters) {
+ final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_PUT, null);
+ e.load_arg(0);
+ e.checkcast(Type.getType(type));
+ e.load_arg(1);
+ e.checkcast(Constants.TYPE_STRING);
+ EmitUtils.string_switch(e, getNames(setters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ PropertyDescriptor pd = (PropertyDescriptor)setters.get(key);
+ if (pd.getReadMethod() == null) {
+ e.aconst_null();
+ } else {
+ MethodInfo read = ReflectUtils.getMethodInfo(pd.getReadMethod());
+ e.dup();
+ e.invoke(read);
+ e.box(read.getSignature().getReturnType());
+ }
+ e.swap(); // move old value behind bean
+ e.load_arg(2); // new value
+ MethodInfo write = ReflectUtils.getMethodInfo(pd.getWriteMethod());
+ e.unbox(write.getSignature().getArgumentTypes()[0]);
+ e.invoke(write);
+ e.return_value();
+ }
+ public void processDefault() {
+ // fall-through
+ }
+ });
+ e.aconst_null();
+ e.return_value();
+ e.end_method();
+ }
+
+ private void generateKeySet(String[] allNames) {
+ // static initializer
+ declare_field(Constants.ACC_STATIC | Constants.ACC_PRIVATE, "keys", FIXED_KEY_SET, null);
+
+ CodeEmitter e = begin_static();
+ e.new_instance(FIXED_KEY_SET);
+ e.dup();
+ EmitUtils.push_array(e, allNames);
+ e.invoke_constructor(FIXED_KEY_SET, CSTRUCT_STRING_ARRAY);
+ e.putfield("keys");
+ e.return_value();
+ e.end_method();
+
+ // keySet
+ e = begin_method(Constants.ACC_PUBLIC, KEY_SET, null);
+ e.load_this();
+ e.getfield("keys");
+ e.return_value();
+ e.end_method();
+ }
+
+ private void generateGetPropertyType(final Map allProps, String[] allNames) {
+ final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_TYPE, null);
+ e.load_arg(0);
+ EmitUtils.string_switch(e, allNames, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ PropertyDescriptor pd = (PropertyDescriptor)allProps.get(key);
+ EmitUtils.load_class(e, Type.getType(pd.getPropertyType()));
+ e.return_value();
+ }
+ public void processDefault() {
+ e.aconst_null();
+ e.return_value();
+ }
+ });
+ e.end_method();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/beans/BulkBean.java b/cglib-and-asm/src/org/mockito/cglib/beans/BulkBean.java
new file mode 100644
index 0000000..f8d053c
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/beans/BulkBean.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.beans;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.cglib.core.*;
+
+/**
+ * @author Juozas Baliuka
+ */
+abstract public class BulkBean
+{
+ private static final BulkBeanKey KEY_FACTORY =
+ (BulkBeanKey)KeyFactory.create(BulkBeanKey.class);
+
+ interface BulkBeanKey {
+ public Object newInstance(String target, String[] getters, String[] setters, String[] types);
+ }
+
+ protected Class target;
+ protected String[] getters, setters;
+ protected Class[] types;
+
+ protected BulkBean() { }
+
+ abstract public void getPropertyValues(Object bean, Object[] values);
+ abstract public void setPropertyValues(Object bean, Object[] values);
+
+ public Object[] getPropertyValues(Object bean) {
+ Object[] values = new Object[getters.length];
+ getPropertyValues(bean, values);
+ return values;
+ }
+
+ public Class[] getPropertyTypes() {
+ return (Class[])types.clone();
+ }
+
+ public String[] getGetters() {
+ return (String[])getters.clone();
+ }
+
+ public String[] getSetters() {
+ return (String[])setters.clone();
+ }
+
+ public static BulkBean create(Class target, String[] getters, String[] setters, Class[] types) {
+ Generator gen = new Generator();
+ gen.setTarget(target);
+ gen.setGetters(getters);
+ gen.setSetters(setters);
+ gen.setTypes(types);
+ return gen.create();
+ }
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(BulkBean.class.getName());
+ private Class target;
+ private String[] getters;
+ private String[] setters;
+ private Class[] types;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ public void setTarget(Class target) {
+ this.target = target;
+ }
+
+ public void setGetters(String[] getters) {
+ this.getters = getters;
+ }
+
+ public void setSetters(String[] setters) {
+ this.setters = setters;
+ }
+
+ public void setTypes(Class[] types) {
+ this.types = types;
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return target.getClassLoader();
+ }
+
+ public BulkBean create() {
+ setNamePrefix(target.getName());
+ String targetClassName = target.getName();
+ String[] typeClassNames = ReflectUtils.getNames(types);
+ Object key = KEY_FACTORY.newInstance(targetClassName, getters, setters, typeClassNames);
+ return (BulkBean)super.create(key);
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ new BulkBeanEmitter(v, getClassName(), target, getters, setters, types);
+ }
+
+ protected Object firstInstance(Class type) {
+ BulkBean instance = (BulkBean)ReflectUtils.newInstance(type);
+ instance.target = target;
+
+ int length = getters.length;
+ instance.getters = new String[length];
+ System.arraycopy(getters, 0, instance.getters, 0, length);
+
+ instance.setters = new String[length];
+ System.arraycopy(setters, 0, instance.setters, 0, length);
+
+ instance.types = new Class[types.length];
+ System.arraycopy(types, 0, instance.types, 0, types.length);
+
+ return instance;
+ }
+
+ protected Object nextInstance(Object instance) {
+ return instance;
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/beans/BulkBeanEmitter.java b/cglib-and-asm/src/org/mockito/cglib/beans/BulkBeanEmitter.java
new file mode 100644
index 0000000..06f0873
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/beans/BulkBeanEmitter.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.beans;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+class BulkBeanEmitter extends ClassEmitter {
+ private static final Signature GET_PROPERTY_VALUES =
+ TypeUtils.parseSignature("void getPropertyValues(Object, Object[])");
+ private static final Signature SET_PROPERTY_VALUES =
+ TypeUtils.parseSignature("void setPropertyValues(Object, Object[])");
+ private static final Signature CSTRUCT_EXCEPTION =
+ TypeUtils.parseConstructor("Throwable, int");
+ private static final Type BULK_BEAN =
+ TypeUtils.parseType("org.mockito.cglib.beans.BulkBean");
+ private static final Type BULK_BEAN_EXCEPTION =
+ TypeUtils.parseType("org.mockito.cglib.beans.BulkBeanException");
+
+ public BulkBeanEmitter(ClassVisitor v,
+ String className,
+ Class target,
+ String[] getterNames,
+ String[] setterNames,
+ Class[] types) {
+ super(v);
+
+ Method[] getters = new Method[getterNames.length];
+ Method[] setters = new Method[setterNames.length];
+ validate(target, getterNames, setterNames, types, getters, setters);
+
+ begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, BULK_BEAN, null, Constants.SOURCE_FILE);
+ EmitUtils.null_constructor(this);
+ generateGet(target, getters);
+ generateSet(target, setters);
+ end_class();
+ }
+
+ private void generateGet(final Class target, final Method[] getters) {
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_VALUES, null);
+ if (getters.length >= 0) {
+ e.load_arg(0);
+ e.checkcast(Type.getType(target));
+ Local bean = e.make_local();
+ e.store_local(bean);
+ for (int i = 0; i < getters.length; i++) {
+ if (getters[i] != null) {
+ MethodInfo getter = ReflectUtils.getMethodInfo(getters[i]);
+ e.load_arg(1);
+ e.push(i);
+ e.load_local(bean);
+ e.invoke(getter);
+ e.box(getter.getSignature().getReturnType());
+ e.aastore();
+ }
+ }
+ }
+ e.return_value();
+ e.end_method();
+ }
+
+ private void generateSet(final Class target, final Method[] setters) {
+ // setPropertyValues
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SET_PROPERTY_VALUES, null);
+ if (setters.length > 0) {
+ Local index = e.make_local(Type.INT_TYPE);
+ e.push(0);
+ e.store_local(index);
+ e.load_arg(0);
+ e.checkcast(Type.getType(target));
+ e.load_arg(1);
+ Block handler = e.begin_block();
+ int lastIndex = 0;
+ for (int i = 0; i < setters.length; i++) {
+ if (setters[i] != null) {
+ MethodInfo setter = ReflectUtils.getMethodInfo(setters[i]);
+ int diff = i - lastIndex;
+ if (diff > 0) {
+ e.iinc(index, diff);
+ lastIndex = i;
+ }
+ e.dup2();
+ e.aaload(i);
+ e.unbox(setter.getSignature().getArgumentTypes()[0]);
+ e.invoke(setter);
+ }
+ }
+ handler.end();
+ e.return_value();
+ e.catch_exception(handler, Constants.TYPE_THROWABLE);
+ e.new_instance(BULK_BEAN_EXCEPTION);
+ e.dup_x1();
+ e.swap();
+ e.load_local(index);
+ e.invoke_constructor(BULK_BEAN_EXCEPTION, CSTRUCT_EXCEPTION);
+ e.athrow();
+ } else {
+ e.return_value();
+ }
+ e.end_method();
+ }
+
+ private static void validate(Class target,
+ String[] getters,
+ String[] setters,
+ Class[] types,
+ Method[] getters_out,
+ Method[] setters_out) {
+ int i = -1;
+ if (setters.length != types.length || getters.length != types.length) {
+ throw new BulkBeanException("accessor array length must be equal type array length", i);
+ }
+ try {
+ for (i = 0; i < types.length; i++) {
+ if (getters[i] != null) {
+ Method method = ReflectUtils.findDeclaredMethod(target, getters[i], null);
+ if (method.getReturnType() != types[i]) {
+ throw new BulkBeanException("Specified type " + types[i] +
+ " does not match declared type " + method.getReturnType(), i);
+ }
+ if (Modifier.isPrivate(method.getModifiers())) {
+ throw new BulkBeanException("Property is private", i);
+ }
+ getters_out[i] = method;
+ }
+ if (setters[i] != null) {
+ Method method = ReflectUtils.findDeclaredMethod(target, setters[i], new Class[]{ types[i] });
+ if (Modifier.isPrivate(method.getModifiers()) ){
+ throw new BulkBeanException("Property is private", i);
+ }
+ setters_out[i] = method;
+ }
+ }
+ } catch (NoSuchMethodException e) {
+ throw new BulkBeanException("Cannot find specified property", i);
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/beans/BulkBeanException.java b/cglib-and-asm/src/org/mockito/cglib/beans/BulkBeanException.java
new file mode 100644
index 0000000..8df00dd
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/beans/BulkBeanException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.beans;
+
+import org.mockito.cglib.core.CodeGenerationException;
+
+public class BulkBeanException extends RuntimeException
+{
+ private int index;
+ private Throwable cause;
+
+ public BulkBeanException(String message, int index) {
+ super(message);
+ this.index = index;
+ }
+
+ public BulkBeanException(Throwable cause, int index) {
+ super(cause.getMessage());
+ this.index = index;
+ this.cause = cause;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public Throwable getCause() {
+ return cause;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/beans/FixedKeySet.java b/cglib-and-asm/src/org/mockito/cglib/beans/FixedKeySet.java
new file mode 100644
index 0000000..af371c9
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/beans/FixedKeySet.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.beans;
+
+import java.util.*;
+
+public /* need it for class loading */ class FixedKeySet extends AbstractSet {
+ private Set set;
+ private int size;
+
+ public FixedKeySet(String[] keys) {
+ size = keys.length;
+ set = Collections.unmodifiableSet(new HashSet(Arrays.asList(keys)));
+ }
+
+ public Iterator iterator() {
+ return set.iterator();
+ }
+
+ public int size() {
+ return size;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/beans/ImmutableBean.java b/cglib-and-asm/src/org/mockito/cglib/beans/ImmutableBean.java
new file mode 100644
index 0000000..936478b
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/beans/ImmutableBean.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.beans;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+/**
+ * @author Chris Nokleberg
+ */
+public class ImmutableBean
+{
+ private static final Type ILLEGAL_STATE_EXCEPTION =
+ TypeUtils.parseType("IllegalStateException");
+ private static final Signature CSTRUCT_OBJECT =
+ TypeUtils.parseConstructor("Object");
+ private static final Class[] OBJECT_CLASSES = { Object.class };
+ private static final String FIELD_NAME = "CGLIB$RWBean";
+
+ private ImmutableBean() {
+ }
+
+ public static Object create(Object bean) {
+ Generator gen = new Generator();
+ gen.setBean(bean);
+ return gen.create();
+ }
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(ImmutableBean.class.getName());
+ private Object bean;
+ private Class target;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ public void setBean(Object bean) {
+ this.bean = bean;
+ target = bean.getClass();
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return target.getClassLoader();
+ }
+
+ public Object create() {
+ String name = target.getName();
+ setNamePrefix(name);
+ return super.create(name);
+ }
+
+ public void generateClass(ClassVisitor v) {
+ Type targetType = Type.getType(target);
+ ClassEmitter ce = new ClassEmitter(v);
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ targetType,
+ null,
+ Constants.SOURCE_FILE);
+
+ ce.declare_field(Constants.ACC_FINAL | Constants.ACC_PRIVATE, FIELD_NAME, targetType, null);
+
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null);
+ e.load_this();
+ e.super_invoke_constructor();
+ e.load_this();
+ e.load_arg(0);
+ e.checkcast(targetType);
+ e.putfield(FIELD_NAME);
+ e.return_value();
+ e.end_method();
+
+ PropertyDescriptor[] descriptors = ReflectUtils.getBeanProperties(target);
+ Method[] getters = ReflectUtils.getPropertyMethods(descriptors, true, false);
+ Method[] setters = ReflectUtils.getPropertyMethods(descriptors, false, true);
+
+ for (int i = 0; i < getters.length; i++) {
+ MethodInfo getter = ReflectUtils.getMethodInfo(getters[i]);
+ e = EmitUtils.begin_method(ce, getter, Constants.ACC_PUBLIC);
+ e.load_this();
+ e.getfield(FIELD_NAME);
+ e.invoke(getter);
+ e.return_value();
+ e.end_method();
+ }
+
+ for (int i = 0; i < setters.length; i++) {
+ MethodInfo setter = ReflectUtils.getMethodInfo(setters[i]);
+ e = EmitUtils.begin_method(ce, setter, Constants.ACC_PUBLIC);
+ e.throw_exception(ILLEGAL_STATE_EXCEPTION, "Bean is immutable");
+ e.end_method();
+ }
+
+ ce.end_class();
+ }
+
+ protected Object firstInstance(Class type) {
+ return ReflectUtils.newInstance(type, OBJECT_CLASSES, new Object[]{ bean });
+ }
+
+ // TODO: optimize
+ protected Object nextInstance(Object instance) {
+ return firstInstance(instance.getClass());
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/AbstractClassGenerator.java b/cglib-and-asm/src/org/mockito/cglib/core/AbstractClassGenerator.java
new file mode 100644
index 0000000..44ca7aa
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/AbstractClassGenerator.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.io.*;
+import java.util.*;
+import java.lang.ref.*;
+
+import org.mockito.asm.ClassReader;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.ClassWriter;
+import org.mockito.asm.Type;
+
+/**
+ * Abstract class for all code-generating CGLIB utilities.
+ * In addition to caching generated classes for performance, it provides hooks for
+ * customizing the <code>ClassLoader</code>, name of the generated class, and transformations
+ * applied before generation.
+ */
+abstract public class AbstractClassGenerator
+implements ClassGenerator
+{
+ private static final Object NAME_KEY = new Object();
+ private static final ThreadLocal CURRENT = new ThreadLocal();
+
+ private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE;
+ private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE;
+ private Source source;
+ private ClassLoader classLoader;
+ private String namePrefix;
+ private Object key;
+ private boolean useCache = true;
+ private String className;
+ private boolean attemptLoad;
+
+ protected static class Source {
+ String name;
+ Map cache = new WeakHashMap();
+ public Source(String name) {
+ this.name = name;
+ }
+ }
+
+ protected AbstractClassGenerator(Source source) {
+ this.source = source;
+ }
+
+ protected void setNamePrefix(String namePrefix) {
+ this.namePrefix = namePrefix;
+ }
+
+ final protected String getClassName() {
+ if (className == null)
+ className = getClassName(getClassLoader());
+ return className;
+ }
+
+ private String getClassName(final ClassLoader loader) {
+ final Set nameCache = getClassNameCache(loader);
+ return namingPolicy.getClassName(namePrefix, source.name, key, new Predicate() {
+ public boolean evaluate(Object arg) {
+ return nameCache.contains(arg);
+ }
+ });
+ }
+
+ private Set getClassNameCache(ClassLoader loader) {
+ return (Set)((Map)source.cache.get(loader)).get(NAME_KEY);
+ }
+
+ /**
+ * Set the <code>ClassLoader</code> in which the class will be generated.
+ * Concrete subclasses of <code>AbstractClassGenerator</code> (such as <code>Enhancer</code>)
+ * will try to choose an appropriate default if this is unset.
+ * <p>
+ * Classes are cached per-<code>ClassLoader</code> using a <code>WeakHashMap</code>, to allow
+ * the generated classes to be removed when the associated loader is garbage collected.
+ * @param classLoader the loader to generate the new class with, or null to use the default
+ */
+ public void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * Override the default naming policy.
+ * @see DefaultNamingPolicy
+ * @param namingPolicy the custom policy, or null to use the default
+ */
+ public void setNamingPolicy(NamingPolicy namingPolicy) {
+ if (namingPolicy == null)
+ namingPolicy = DefaultNamingPolicy.INSTANCE;
+ this.namingPolicy = namingPolicy;
+ }
+
+ /**
+ * @see #setNamingPolicy
+ */
+ public NamingPolicy getNamingPolicy() {
+ return namingPolicy;
+ }
+
+ /**
+ * Whether use and update the static cache of generated classes
+ * for a class with the same properties. Default is <code>true</code>.
+ */
+ public void setUseCache(boolean useCache) {
+ this.useCache = useCache;
+ }
+
+ /**
+ * @see #setUseCache
+ */
+ public boolean getUseCache() {
+ return useCache;
+ }
+
+ /**
+ * If set, CGLIB will attempt to load classes from the specified
+ * <code>ClassLoader</code> before generating them. Because generated
+ * class names are not guaranteed to be unique, the default is <code>false</code>.
+ */
+ public void setAttemptLoad(boolean attemptLoad) {
+ this.attemptLoad = attemptLoad;
+ }
+
+ public boolean getAttemptLoad() {
+ return attemptLoad;
+ }
+
+ /**
+ * Set the strategy to use to create the bytecode from this generator.
+ * By default an instance of {@see DefaultGeneratorStrategy} is used.
+ */
+ public void setStrategy(GeneratorStrategy strategy) {
+ if (strategy == null)
+ strategy = DefaultGeneratorStrategy.INSTANCE;
+ this.strategy = strategy;
+ }
+
+ /**
+ * @see #setStrategy
+ */
+ public GeneratorStrategy getStrategy() {
+ return strategy;
+ }
+
+ /**
+ * Used internally by CGLIB. Returns the <code>AbstractClassGenerator</code>
+ * that is being used to generate a class in the current thread.
+ */
+ public static AbstractClassGenerator getCurrent() {
+ return (AbstractClassGenerator)CURRENT.get();
+ }
+
+ public ClassLoader getClassLoader() {
+ ClassLoader t = classLoader;
+ if (t == null) {
+ t = getDefaultClassLoader();
+ }
+ if (t == null) {
+ t = getClass().getClassLoader();
+ }
+ if (t == null) {
+ t = Thread.currentThread().getContextClassLoader();
+ }
+ if (t == null) {
+ throw new IllegalStateException("Cannot determine classloader");
+ }
+ return t;
+ }
+
+ abstract protected ClassLoader getDefaultClassLoader();
+
+ protected Object create(Object key) {
+ try {
+ Class gen = null;
+
+ synchronized (source) {
+ ClassLoader loader = getClassLoader();
+ Map cache2 = null;
+ cache2 = (Map)source.cache.get(loader);
+ if (cache2 == null) {
+ cache2 = new HashMap();
+ cache2.put(NAME_KEY, new HashSet());
+ source.cache.put(loader, cache2);
+ } else if (useCache) {
+ Reference ref = (Reference)cache2.get(key);
+ gen = (Class) (( ref == null ) ? null : ref.get());
+ }
+ if (gen == null) {
+ Object save = CURRENT.get();
+ CURRENT.set(this);
+ try {
+ this.key = key;
+
+ if (attemptLoad) {
+ try {
+ gen = loader.loadClass(getClassName());
+ } catch (ClassNotFoundException e) {
+ // ignore
+ }
+ }
+ if (gen == null) {
+ byte[] b = strategy.generate(this);
+ String className = ClassNameReader.getClassName(new ClassReader(b));
+ getClassNameCache(loader).add(className);
+ gen = ReflectUtils.defineClass(className, b, loader);
+ }
+
+ if (useCache) {
+ cache2.put(key, new WeakReference(gen));
+ }
+ return firstInstance(gen);
+ } finally {
+ CURRENT.set(save);
+ }
+ }
+ }
+ return firstInstance(gen);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Error e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ abstract protected Object firstInstance(Class type) throws Exception;
+ abstract protected Object nextInstance(Object instance) throws Exception;
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/Block.java b/cglib-and-asm/src/org/mockito/cglib/core/Block.java
new file mode 100644
index 0000000..16dfbec
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/Block.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.Label;
+
+public class Block
+{
+ private CodeEmitter e;
+ private Label start;
+ private Label end;
+
+ public Block(CodeEmitter e) {
+ this.e = e;
+ start = e.mark();
+ }
+
+ public CodeEmitter getCodeEmitter() {
+ return e;
+ }
+
+ public void end() {
+ if (end != null) {
+ throw new IllegalStateException("end of label already set");
+ }
+ end = e.mark();
+ }
+
+ public Label getStart() {
+ return start;
+ }
+
+ public Label getEnd() {
+ return end;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/ClassEmitter.java b/cglib-and-asm/src/org/mockito/cglib/core/ClassEmitter.java
new file mode 100644
index 0000000..d13891a
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/ClassEmitter.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.io.*;
+import java.util.*;
+
+import org.mockito.asm.*;
+
+/**
+ * @author Juozas Baliuka, Chris Nokleberg
+ */
+public class ClassEmitter extends ClassAdapter {
+ private ClassInfo classInfo;
+ private Map fieldInfo;
+
+ private static int hookCounter;
+ private MethodVisitor rawStaticInit;
+ private CodeEmitter staticInit;
+ private CodeEmitter staticHook;
+ private Signature staticHookSig;
+
+ public ClassEmitter(ClassVisitor cv) {
+ super(null);
+ setTarget(cv);
+ }
+
+ public ClassEmitter() {
+ super(null);
+ }
+
+ public void setTarget(ClassVisitor cv) {
+ this.cv = cv;
+ fieldInfo = new HashMap();
+
+ // just to be safe
+ staticInit = staticHook = null;
+ staticHookSig = null;
+ }
+
+ synchronized private static int getNextHook() {
+ return ++hookCounter;
+ }
+
+ public ClassInfo getClassInfo() {
+ return classInfo;
+ }
+
+ public void begin_class(int version, final int access, String className, final Type superType, final Type[] interfaces, String source) {
+ final Type classType = Type.getType("L" + className.replace('.', '/') + ";");
+ classInfo = new ClassInfo() {
+ public Type getType() {
+ return classType;
+ }
+ public Type getSuperType() {
+ return (superType != null) ? superType : Constants.TYPE_OBJECT;
+ }
+ public Type[] getInterfaces() {
+ return interfaces;
+ }
+ public int getModifiers() {
+ return access;
+ }
+ };
+ cv.visit(version,
+ access,
+ classInfo.getType().getInternalName(),
+ null,
+ classInfo.getSuperType().getInternalName(),
+ TypeUtils.toInternalNames(interfaces));
+ if (source != null)
+ cv.visitSource(source, null);
+ init();
+ }
+
+ public CodeEmitter getStaticHook() {
+ if (TypeUtils.isInterface(getAccess())) {
+ throw new IllegalStateException("static hook is invalid for this class");
+ }
+ if (staticHook == null) {
+ staticHookSig = new Signature("CGLIB$STATICHOOK" + getNextHook(), "()V");
+ staticHook = begin_method(Constants.ACC_STATIC,
+ staticHookSig,
+ null);
+ if (staticInit != null) {
+ staticInit.invoke_static_this(staticHookSig);
+ }
+ }
+ return staticHook;
+ }
+
+ protected void init() {
+ }
+
+ public int getAccess() {
+ return classInfo.getModifiers();
+ }
+
+ public Type getClassType() {
+ return classInfo.getType();
+ }
+
+ public Type getSuperType() {
+ return classInfo.getSuperType();
+ }
+
+ public void end_class() {
+ if (staticHook != null && staticInit == null) {
+ // force creation of static init
+ begin_static();
+ }
+ if (staticInit != null) {
+ staticHook.return_value();
+ staticHook.end_method();
+ rawStaticInit.visitInsn(Constants.RETURN);
+ rawStaticInit.visitMaxs(0, 0);
+ staticInit = staticHook = null;
+ staticHookSig = null;
+ }
+ cv.visitEnd();
+ }
+
+ public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) {
+ if (classInfo == null)
+ throw new IllegalStateException("classInfo is null! " + this);
+ MethodVisitor v = cv.visitMethod(access,
+ sig.getName(),
+ sig.getDescriptor(),
+ null,
+ TypeUtils.toInternalNames(exceptions));
+ if (sig.equals(Constants.SIG_STATIC) && !TypeUtils.isInterface(getAccess())) {
+ rawStaticInit = v;
+ MethodVisitor wrapped = new MethodAdapter(v) {
+ public void visitMaxs(int maxStack, int maxLocals) {
+ // ignore
+ }
+ public void visitInsn(int insn) {
+ if (insn != Constants.RETURN) {
+ super.visitInsn(insn);
+ }
+ }
+ };
+ staticInit = new CodeEmitter(this, wrapped, access, sig, exceptions);
+ if (staticHook == null) {
+ // force static hook creation
+ getStaticHook();
+ } else {
+ staticInit.invoke_static_this(staticHookSig);
+ }
+ return staticInit;
+ } else if (sig.equals(staticHookSig)) {
+ return new CodeEmitter(this, v, access, sig, exceptions) {
+ public boolean isStaticHook() {
+ return true;
+ }
+ };
+ } else {
+ return new CodeEmitter(this, v, access, sig, exceptions);
+ }
+ }
+
+ public CodeEmitter begin_static() {
+ return begin_method(Constants.ACC_STATIC, Constants.SIG_STATIC, null);
+ }
+
+ public void declare_field(int access, String name, Type type, Object value) {
+ FieldInfo existing = (FieldInfo)fieldInfo.get(name);
+ FieldInfo info = new FieldInfo(access, name, type, value);
+ if (existing != null) {
+ if (!info.equals(existing)) {
+ throw new IllegalArgumentException("Field \"" + name + "\" has been declared differently");
+ }
+ } else {
+ fieldInfo.put(name, info);
+ cv.visitField(access, name, type.getDescriptor(), null, value);
+ }
+ }
+
+ // TODO: make public?
+ boolean isFieldDeclared(String name) {
+ return fieldInfo.get(name) != null;
+ }
+
+ FieldInfo getFieldInfo(String name) {
+ FieldInfo field = (FieldInfo)fieldInfo.get(name);
+ if (field == null) {
+ throw new IllegalArgumentException("Field " + name + " is not declared in " + getClassType().getClassName());
+ }
+ return field;
+ }
+
+ static class FieldInfo {
+ int access;
+ String name;
+ Type type;
+ Object value;
+
+ public FieldInfo(int access, String name, Type type, Object value) {
+ this.access = access;
+ this.name = name;
+ this.type = type;
+ this.value = value;
+ }
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (!(o instanceof FieldInfo))
+ return false;
+ FieldInfo other = (FieldInfo)o;
+ if (access != other.access ||
+ !name.equals(other.name) ||
+ !type.equals(other.type)) {
+ return false;
+ }
+ if ((value == null) ^ (other.value == null))
+ return false;
+ if (value != null && !value.equals(other.value))
+ return false;
+ return true;
+ }
+
+ public int hashCode() {
+ return access ^ name.hashCode() ^ type.hashCode() ^ ((value == null) ? 0 : value.hashCode());
+ }
+ }
+
+ public void visit(int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ begin_class(version,
+ access,
+ name.replace('/', '.'),
+ TypeUtils.fromInternalName(superName),
+ TypeUtils.fromInternalNames(interfaces),
+ null); // TODO
+ }
+
+ public void visitEnd() {
+ end_class();
+ }
+
+ public FieldVisitor visitField(int access,
+ String name,
+ String desc,
+ String signature,
+ Object value) {
+ declare_field(access, name, Type.getType(desc), value);
+ return null; // TODO
+ }
+
+ public MethodVisitor visitMethod(int access,
+ String name,
+ String desc,
+ String signature,
+ String[] exceptions) {
+ return begin_method(access,
+ new Signature(name, desc),
+ TypeUtils.fromInternalNames(exceptions));
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/ClassGenerator.java b/cglib-and-asm/src/org/mockito/cglib/core/ClassGenerator.java
new file mode 100644
index 0000000..116c19d
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/ClassGenerator.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.ClassVisitor;
+
+public interface ClassGenerator {
+ void generateClass(ClassVisitor v) throws Exception;
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/ClassInfo.java b/cglib-and-asm/src/org/mockito/cglib/core/ClassInfo.java
new file mode 100644
index 0000000..4cb763c
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/ClassInfo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.Type;
+
+abstract public class ClassInfo {
+
+ protected ClassInfo() {
+ }
+
+ abstract public Type getType();
+ abstract public Type getSuperType();
+ abstract public Type[] getInterfaces();
+ abstract public int getModifiers();
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (!(o instanceof ClassInfo))
+ return false;
+ return getType().equals(((ClassInfo)o).getType());
+ }
+
+ public int hashCode() {
+ return getType().hashCode();
+ }
+
+ public String toString() {
+ // TODO: include modifiers, superType, interfaces
+ return getType().getClassName();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/ClassNameReader.java b/cglib-and-asm/src/org/mockito/cglib/core/ClassNameReader.java
new file mode 100644
index 0000000..11073f7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/ClassNameReader.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.ClassAdapter;
+import org.mockito.asm.ClassReader;
+
+import java.util.*;
+
+// TODO: optimize (ClassReader buffers entire class before accept)
+public class ClassNameReader {
+ private ClassNameReader() {
+ }
+
+ private static final EarlyExitException EARLY_EXIT = new EarlyExitException();
+ private static class EarlyExitException extends RuntimeException { }
+
+ public static String getClassName(ClassReader r) {
+
+ return getClassInfo(r)[0];
+
+ }
+
+ public static String[] getClassInfo(ClassReader r) {
+ final List array = new ArrayList();
+ try {
+ r.accept(new ClassAdapter(null) {
+ public void visit(int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ array.add( name.replace('/', '.') );
+ if(superName != null){
+ array.add( superName.replace('/', '.') );
+ }
+ for(int i = 0; i < interfaces.length; i++ ){
+ array.add( interfaces[i].replace('/', '.') );
+ }
+
+ throw EARLY_EXIT;
+ }
+ }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ } catch (EarlyExitException e) { }
+
+ return (String[])array.toArray( new String[]{} );
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/ClassesKey.java b/cglib-and-asm/src/org/mockito/cglib/core/ClassesKey.java
new file mode 100644
index 0000000..4abf507
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/ClassesKey.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+public class ClassesKey {
+ private static final Key FACTORY = (Key)KeyFactory.create(Key.class, KeyFactory.OBJECT_BY_CLASS);
+
+ interface Key {
+ Object newInstance(Object[] array);
+ }
+
+ private ClassesKey() {
+ }
+
+ public static Object create(Object[] array) {
+ return FACTORY.newInstance(array);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/CodeEmitter.java b/cglib-and-asm/src/org/mockito/cglib/core/CodeEmitter.java
new file mode 100644
index 0000000..c94e91a
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/CodeEmitter.java
@@ -0,0 +1,864 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.io.*;
+import java.util.*;
+
+import org.mockito.asm.*;
+
+/**
+ * @author Juozas Baliuka, Chris Nokleberg
+ */
+public class CodeEmitter extends LocalVariablesSorter {
+ private static final Signature BOOLEAN_VALUE =
+ TypeUtils.parseSignature("boolean booleanValue()");
+ private static final Signature CHAR_VALUE =
+ TypeUtils.parseSignature("char charValue()");
+ private static final Signature LONG_VALUE =
+ TypeUtils.parseSignature("long longValue()");
+ private static final Signature DOUBLE_VALUE =
+ TypeUtils.parseSignature("double doubleValue()");
+ private static final Signature FLOAT_VALUE =
+ TypeUtils.parseSignature("float floatValue()");
+ private static final Signature INT_VALUE =
+ TypeUtils.parseSignature("int intValue()");
+ private static final Signature CSTRUCT_NULL =
+ TypeUtils.parseConstructor("");
+ private static final Signature CSTRUCT_STRING =
+ TypeUtils.parseConstructor("String");
+
+ public static final int ADD = Constants.IADD;
+ public static final int MUL = Constants.IMUL;
+ public static final int XOR = Constants.IXOR;
+ public static final int USHR = Constants.IUSHR;
+ public static final int SUB = Constants.ISUB;
+ public static final int DIV = Constants.IDIV;
+ public static final int NEG = Constants.INEG;
+ public static final int REM = Constants.IREM;
+ public static final int AND = Constants.IAND;
+ public static final int OR = Constants.IOR;
+
+ public static final int GT = Constants.IFGT;
+ public static final int LT = Constants.IFLT;
+ public static final int GE = Constants.IFGE;
+ public static final int LE = Constants.IFLE;
+ public static final int NE = Constants.IFNE;
+ public static final int EQ = Constants.IFEQ;
+
+ private ClassEmitter ce;
+ private State state;
+
+ private static class State
+ extends MethodInfo
+ {
+ ClassInfo classInfo;
+ int access;
+ Signature sig;
+ Type[] argumentTypes;
+ int localOffset;
+ Type[] exceptionTypes;
+
+ State(ClassInfo classInfo, int access, Signature sig, Type[] exceptionTypes) {
+ this.classInfo = classInfo;
+ this.access = access;
+ this.sig = sig;
+ this.exceptionTypes = exceptionTypes;
+ localOffset = TypeUtils.isStatic(access) ? 0 : 1;
+ argumentTypes = sig.getArgumentTypes();
+ }
+
+ public ClassInfo getClassInfo() {
+ return classInfo;
+ }
+
+ public int getModifiers() {
+ return access;
+ }
+
+ public Signature getSignature() {
+ return sig;
+ }
+
+ public Type[] getExceptionTypes() {
+ return exceptionTypes;
+ }
+
+ public Attribute getAttribute() {
+ // TODO
+ return null;
+ }
+ }
+
+ CodeEmitter(ClassEmitter ce, MethodVisitor mv, int access, Signature sig, Type[] exceptionTypes) {
+ super(access, sig.getDescriptor(), mv);
+ this.ce = ce;
+ state = new State(ce.getClassInfo(), access, sig, exceptionTypes);
+ }
+
+ public CodeEmitter(CodeEmitter wrap) {
+ super(wrap);
+ this.ce = wrap.ce;
+ this.state = wrap.state;
+ }
+
+ public boolean isStaticHook() {
+ return false;
+ }
+
+ public Signature getSignature() {
+ return state.sig;
+ }
+
+ public Type getReturnType() {
+ return state.sig.getReturnType();
+ }
+
+ public MethodInfo getMethodInfo() {
+ return state;
+ }
+
+ public ClassEmitter getClassEmitter() {
+ return ce;
+ }
+
+ public void end_method() {
+ visitMaxs(0, 0);
+ }
+
+ public Block begin_block() {
+ return new Block(this);
+ }
+
+ public void catch_exception(Block block, Type exception) {
+ if (block.getEnd() == null) {
+ throw new IllegalStateException("end of block is unset");
+ }
+ mv.visitTryCatchBlock(block.getStart(),
+ block.getEnd(),
+ mark(),
+ exception.getInternalName());
+ }
+
+ public void goTo(Label label) { mv.visitJumpInsn(Constants.GOTO, label); }
+ public void ifnull(Label label) { mv.visitJumpInsn(Constants.IFNULL, label); }
+ public void ifnonnull(Label label) { mv.visitJumpInsn(Constants.IFNONNULL, label); }
+
+ public void if_jump(int mode, Label label) {
+ mv.visitJumpInsn(mode, label);
+ }
+
+ public void if_icmp(int mode, Label label) {
+ if_cmp(Type.INT_TYPE, mode, label);
+ }
+
+ public void if_cmp(Type type, int mode, Label label) {
+ int intOp = -1;
+ int jumpmode = mode;
+ switch (mode) {
+ case GE: jumpmode = LT; break;
+ case LE: jumpmode = GT; break;
+ }
+ switch (type.getSort()) {
+ case Type.LONG:
+ mv.visitInsn(Constants.LCMP);
+ break;
+ case Type.DOUBLE:
+ mv.visitInsn(Constants.DCMPG);
+ break;
+ case Type.FLOAT:
+ mv.visitInsn(Constants.FCMPG);
+ break;
+ case Type.ARRAY:
+ case Type.OBJECT:
+ switch (mode) {
+ case EQ:
+ mv.visitJumpInsn(Constants.IF_ACMPEQ, label);
+ return;
+ case NE:
+ mv.visitJumpInsn(Constants.IF_ACMPNE, label);
+ return;
+ }
+ throw new IllegalArgumentException("Bad comparison for type " + type);
+ default:
+ switch (mode) {
+ case EQ: intOp = Constants.IF_ICMPEQ; break;
+ case NE: intOp = Constants.IF_ICMPNE; break;
+ case GE: swap(); /* fall through */
+ case LT: intOp = Constants.IF_ICMPLT; break;
+ case LE: swap(); /* fall through */
+ case GT: intOp = Constants.IF_ICMPGT; break;
+ }
+ mv.visitJumpInsn(intOp, label);
+ return;
+ }
+ if_jump(jumpmode, label);
+ }
+
+ public void pop() { mv.visitInsn(Constants.POP); }
+ public void pop2() { mv.visitInsn(Constants.POP2); }
+ public void dup() { mv.visitInsn(Constants.DUP); }
+ public void dup2() { mv.visitInsn(Constants.DUP2); }
+ public void dup_x1() { mv.visitInsn(Constants.DUP_X1); }
+ public void dup_x2() { mv.visitInsn(Constants.DUP_X2); }
+ public void dup2_x1() { mv.visitInsn(Constants.DUP2_X1); }
+ public void dup2_x2() { mv.visitInsn(Constants.DUP2_X2); }
+ public void swap() { mv.visitInsn(Constants.SWAP); }
+ public void aconst_null() { mv.visitInsn(Constants.ACONST_NULL); }
+
+ public void swap(Type prev, Type type) {
+ if (type.getSize() == 1) {
+ if (prev.getSize() == 1) {
+ swap(); // same as dup_x1(), pop();
+ } else {
+ dup_x2();
+ pop();
+ }
+ } else {
+ if (prev.getSize() == 1) {
+ dup2_x1();
+ pop2();
+ } else {
+ dup2_x2();
+ pop2();
+ }
+ }
+ }
+
+ public void monitorenter() { mv.visitInsn(Constants.MONITORENTER); }
+ public void monitorexit() { mv.visitInsn(Constants.MONITOREXIT); }
+
+ public void math(int op, Type type) { mv.visitInsn(type.getOpcode(op)); }
+
+ public void array_load(Type type) { mv.visitInsn(type.getOpcode(Constants.IALOAD)); }
+ public void array_store(Type type) { mv.visitInsn(type.getOpcode(Constants.IASTORE)); }
+
+ /**
+ * Casts from one primitive numeric type to another
+ */
+ public void cast_numeric(Type from, Type to) {
+ if (from != to) {
+ if (from == Type.DOUBLE_TYPE) {
+ if (to == Type.FLOAT_TYPE) {
+ mv.visitInsn(Constants.D2F);
+ } else if (to == Type.LONG_TYPE) {
+ mv.visitInsn(Constants.D2L);
+ } else {
+ mv.visitInsn(Constants.D2I);
+ cast_numeric(Type.INT_TYPE, to);
+ }
+ } else if (from == Type.FLOAT_TYPE) {
+ if (to == Type.DOUBLE_TYPE) {
+ mv.visitInsn(Constants.F2D);
+ } else if (to == Type.LONG_TYPE) {
+ mv.visitInsn(Constants.F2L);
+ } else {
+ mv.visitInsn(Constants.F2I);
+ cast_numeric(Type.INT_TYPE, to);
+ }
+ } else if (from == Type.LONG_TYPE) {
+ if (to == Type.DOUBLE_TYPE) {
+ mv.visitInsn(Constants.L2D);
+ } else if (to == Type.FLOAT_TYPE) {
+ mv.visitInsn(Constants.L2F);
+ } else {
+ mv.visitInsn(Constants.L2I);
+ cast_numeric(Type.INT_TYPE, to);
+ }
+ } else {
+ if (to == Type.BYTE_TYPE) {
+ mv.visitInsn(Constants.I2B);
+ } else if (to == Type.CHAR_TYPE) {
+ mv.visitInsn(Constants.I2C);
+ } else if (to == Type.DOUBLE_TYPE) {
+ mv.visitInsn(Constants.I2D);
+ } else if (to == Type.FLOAT_TYPE) {
+ mv.visitInsn(Constants.I2F);
+ } else if (to == Type.LONG_TYPE) {
+ mv.visitInsn(Constants.I2L);
+ } else if (to == Type.SHORT_TYPE) {
+ mv.visitInsn(Constants.I2S);
+ }
+ }
+ }
+ }
+
+ public void push(int i) {
+ if (i < -1) {
+ mv.visitLdcInsn(new Integer(i));
+ } else if (i <= 5) {
+ mv.visitInsn(TypeUtils.ICONST(i));
+ } else if (i <= Byte.MAX_VALUE) {
+ mv.visitIntInsn(Constants.BIPUSH, i);
+ } else if (i <= Short.MAX_VALUE) {
+ mv.visitIntInsn(Constants.SIPUSH, i);
+ } else {
+ mv.visitLdcInsn(new Integer(i));
+ }
+ }
+
+ public void push(long value) {
+ if (value == 0L || value == 1L) {
+ mv.visitInsn(TypeUtils.LCONST(value));
+ } else {
+ mv.visitLdcInsn(new Long(value));
+ }
+ }
+
+ public void push(float value) {
+ if (value == 0f || value == 1f || value == 2f) {
+ mv.visitInsn(TypeUtils.FCONST(value));
+ } else {
+ mv.visitLdcInsn(new Float(value));
+ }
+ }
+ public void push(double value) {
+ if (value == 0d || value == 1d) {
+ mv.visitInsn(TypeUtils.DCONST(value));
+ } else {
+ mv.visitLdcInsn(new Double(value));
+ }
+ }
+
+ public void push(String value) {
+ mv.visitLdcInsn(value);
+ }
+
+ public void newarray() {
+ newarray(Constants.TYPE_OBJECT);
+ }
+
+ public void newarray(Type type) {
+ if (TypeUtils.isPrimitive(type)) {
+ mv.visitIntInsn(Constants.NEWARRAY, TypeUtils.NEWARRAY(type));
+ } else {
+ emit_type(Constants.ANEWARRAY, type);
+ }
+ }
+
+ public void arraylength() {
+ mv.visitInsn(Constants.ARRAYLENGTH);
+ }
+
+ public void load_this() {
+ if (TypeUtils.isStatic(state.access)) {
+ throw new IllegalStateException("no 'this' pointer within static method");
+ }
+ mv.visitVarInsn(Constants.ALOAD, 0);
+ }
+
+ /**
+ * Pushes all of the arguments of the current method onto the stack.
+ */
+ public void load_args() {
+ load_args(0, state.argumentTypes.length);
+ }
+
+ /**
+ * Pushes the specified argument of the current method onto the stack.
+ * @param index the zero-based index into the argument list
+ */
+ public void load_arg(int index) {
+ load_local(state.argumentTypes[index],
+ state.localOffset + skipArgs(index));
+ }
+
+ // zero-based (see load_this)
+ public void load_args(int fromArg, int count) {
+ int pos = state.localOffset + skipArgs(fromArg);
+ for (int i = 0; i < count; i++) {
+ Type t = state.argumentTypes[fromArg + i];
+ load_local(t, pos);
+ pos += t.getSize();
+ }
+ }
+
+ private int skipArgs(int numArgs) {
+ int amount = 0;
+ for (int i = 0; i < numArgs; i++) {
+ amount += state.argumentTypes[i].getSize();
+ }
+ return amount;
+ }
+
+ private void load_local(Type t, int pos) {
+ // TODO: make t == null ok?
+ mv.visitVarInsn(t.getOpcode(Constants.ILOAD), pos);
+ }
+
+ private void store_local(Type t, int pos) {
+ // TODO: make t == null ok?
+ mv.visitVarInsn(t.getOpcode(Constants.ISTORE), pos);
+ }
+
+ public void iinc(Local local, int amount) {
+ mv.visitIincInsn(local.getIndex(), amount);
+ }
+
+ public void store_local(Local local) {
+ store_local(local.getType(), local.getIndex());
+ }
+
+ public void load_local(Local local) {
+ load_local(local.getType(), local.getIndex());
+ }
+
+ public void return_value() {
+ mv.visitInsn(state.sig.getReturnType().getOpcode(Constants.IRETURN));
+ }
+
+ public void getfield(String name) {
+ ClassEmitter.FieldInfo info = ce.getFieldInfo(name);
+ int opcode = TypeUtils.isStatic(info.access) ? Constants.GETSTATIC : Constants.GETFIELD;
+ emit_field(opcode, ce.getClassType(), name, info.type);
+ }
+
+ public void putfield(String name) {
+ ClassEmitter.FieldInfo info = ce.getFieldInfo(name);
+ int opcode = TypeUtils.isStatic(info.access) ? Constants.PUTSTATIC : Constants.PUTFIELD;
+ emit_field(opcode, ce.getClassType(), name, info.type);
+ }
+
+ public void super_getfield(String name, Type type) {
+ emit_field(Constants.GETFIELD, ce.getSuperType(), name, type);
+ }
+
+ public void super_putfield(String name, Type type) {
+ emit_field(Constants.PUTFIELD, ce.getSuperType(), name, type);
+ }
+
+ public void super_getstatic(String name, Type type) {
+ emit_field(Constants.GETSTATIC, ce.getSuperType(), name, type);
+ }
+
+ public void super_putstatic(String name, Type type) {
+ emit_field(Constants.PUTSTATIC, ce.getSuperType(), name, type);
+ }
+
+ public void getfield(Type owner, String name, Type type) {
+ emit_field(Constants.GETFIELD, owner, name, type);
+ }
+
+ public void putfield(Type owner, String name, Type type) {
+ emit_field(Constants.PUTFIELD, owner, name, type);
+ }
+
+ public void getstatic(Type owner, String name, Type type) {
+ emit_field(Constants.GETSTATIC, owner, name, type);
+ }
+
+ public void putstatic(Type owner, String name, Type type) {
+ emit_field(Constants.PUTSTATIC, owner, name, type);
+ }
+
+ // package-protected for EmitUtils, try to fix
+ void emit_field(int opcode, Type ctype, String name, Type ftype) {
+ mv.visitFieldInsn(opcode,
+ ctype.getInternalName(),
+ name,
+ ftype.getDescriptor());
+ }
+
+ public void super_invoke() {
+ super_invoke(state.sig);
+ }
+
+ public void super_invoke(Signature sig) {
+ emit_invoke(Constants.INVOKESPECIAL, ce.getSuperType(), sig);
+ }
+
+ public void invoke_constructor(Type type) {
+ invoke_constructor(type, CSTRUCT_NULL);
+ }
+
+ public void super_invoke_constructor() {
+ invoke_constructor(ce.getSuperType());
+ }
+
+ public void invoke_constructor_this() {
+ invoke_constructor(ce.getClassType());
+ }
+
+ private void emit_invoke(int opcode, Type type, Signature sig) {
+ if (sig.getName().equals(Constants.CONSTRUCTOR_NAME) &&
+ ((opcode == Constants.INVOKEVIRTUAL) ||
+ (opcode == Constants.INVOKESTATIC))) {
+ // TODO: error
+ }
+ mv.visitMethodInsn(opcode,
+ type.getInternalName(),
+ sig.getName(),
+ sig.getDescriptor());
+ }
+
+ public void invoke_interface(Type owner, Signature sig) {
+ emit_invoke(Constants.INVOKEINTERFACE, owner, sig);
+ }
+
+ public void invoke_virtual(Type owner, Signature sig) {
+ emit_invoke(Constants.INVOKEVIRTUAL, owner, sig);
+ }
+
+ public void invoke_static(Type owner, Signature sig) {
+ emit_invoke(Constants.INVOKESTATIC, owner, sig);
+ }
+
+ public void invoke_virtual_this(Signature sig) {
+ invoke_virtual(ce.getClassType(), sig);
+ }
+
+ public void invoke_static_this(Signature sig) {
+ invoke_static(ce.getClassType(), sig);
+ }
+
+ public void invoke_constructor(Type type, Signature sig) {
+ emit_invoke(Constants.INVOKESPECIAL, type, sig);
+ }
+
+ public void invoke_constructor_this(Signature sig) {
+ invoke_constructor(ce.getClassType(), sig);
+ }
+
+ public void super_invoke_constructor(Signature sig) {
+ invoke_constructor(ce.getSuperType(), sig);
+ }
+
+ public void new_instance_this() {
+ new_instance(ce.getClassType());
+ }
+
+ public void new_instance(Type type) {
+ emit_type(Constants.NEW, type);
+ }
+
+ private void emit_type(int opcode, Type type) {
+ String desc;
+ if (TypeUtils.isArray(type)) {
+ desc = type.getDescriptor();
+ } else {
+ desc = type.getInternalName();
+ }
+ mv.visitTypeInsn(opcode, desc);
+ }
+
+ public void aaload(int index) {
+ push(index);
+ aaload();
+ }
+
+ public void aaload() { mv.visitInsn(Constants.AALOAD); }
+ public void aastore() { mv.visitInsn(Constants.AASTORE); }
+ public void athrow() { mv.visitInsn(Constants.ATHROW); }
+
+ public Label make_label() {
+ return new Label();
+ }
+
+ public Local make_local() {
+ return make_local(Constants.TYPE_OBJECT);
+ }
+
+ public Local make_local(Type type) {
+ return new Local(newLocal(type.getSize()), type);
+ }
+
+ public void checkcast_this() {
+ checkcast(ce.getClassType());
+ }
+
+ public void checkcast(Type type) {
+ if (!type.equals(Constants.TYPE_OBJECT)) {
+ emit_type(Constants.CHECKCAST, type);
+ }
+ }
+
+ public void instance_of(Type type) {
+ emit_type(Constants.INSTANCEOF, type);
+ }
+
+ public void instance_of_this() {
+ instance_of(ce.getClassType());
+ }
+
+ public void process_switch(int[] keys, ProcessSwitchCallback callback) {
+ float density;
+ if (keys.length == 0) {
+ density = 0;
+ } else {
+ density = (float)keys.length / (keys[keys.length - 1] - keys[0] + 1);
+ }
+ process_switch(keys, callback, density >= 0.5f);
+ }
+
+ public void process_switch(int[] keys, ProcessSwitchCallback callback, boolean useTable) {
+ if (!isSorted(keys))
+ throw new IllegalArgumentException("keys to switch must be sorted ascending");
+ Label def = make_label();
+ Label end = make_label();
+
+ try {
+ if (keys.length > 0) {
+ int len = keys.length;
+ int min = keys[0];
+ int max = keys[len - 1];
+ int range = max - min + 1;
+
+ if (useTable) {
+ Label[] labels = new Label[range];
+ Arrays.fill(labels, def);
+ for (int i = 0; i < len; i++) {
+ labels[keys[i] - min] = make_label();
+ }
+ mv.visitTableSwitchInsn(min, max, def, labels);
+ for (int i = 0; i < range; i++) {
+ Label label = labels[i];
+ if (label != def) {
+ mark(label);
+ callback.processCase(i + min, end);
+ }
+ }
+ } else {
+ Label[] labels = new Label[len];
+ for (int i = 0; i < len; i++) {
+ labels[i] = make_label();
+ }
+ mv.visitLookupSwitchInsn(def, keys, labels);
+ for (int i = 0; i < len; i++) {
+ mark(labels[i]);
+ callback.processCase(keys[i], end);
+ }
+ }
+ }
+
+ mark(def);
+ callback.processDefault();
+ mark(end);
+
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Error e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ private static boolean isSorted(int[] keys) {
+ for (int i = 1; i < keys.length; i++) {
+ if (keys[i] < keys[i - 1])
+ return false;
+ }
+ return true;
+ }
+
+ public void mark(Label label) {
+ mv.visitLabel(label);
+ }
+
+ Label mark() {
+ Label label = make_label();
+ mv.visitLabel(label);
+ return label;
+ }
+
+ public void push(boolean value) {
+ push(value ? 1 : 0);
+ }
+
+ /**
+ * Toggles the integer on the top of the stack from 1 to 0 or vice versa
+ */
+ public void not() {
+ push(1);
+ math(XOR, Type.INT_TYPE);
+ }
+
+ public void throw_exception(Type type, String msg) {
+ new_instance(type);
+ dup();
+ push(msg);
+ invoke_constructor(type, CSTRUCT_STRING);
+ athrow();
+ }
+
+ /**
+ * If the argument is a primitive class, replaces the primitive value
+ * on the top of the stack with the wrapped (Object) equivalent. For
+ * example, char -> Character.
+ * If the class is Void, a null is pushed onto the stack instead.
+ * @param type the class indicating the current type of the top stack value
+ */
+ public void box(Type type) {
+ if (TypeUtils.isPrimitive(type)) {
+ if (type == Type.VOID_TYPE) {
+ aconst_null();
+ } else {
+ Type boxed = TypeUtils.getBoxedType(type);
+ new_instance(boxed);
+ if (type.getSize() == 2) {
+ // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
+ dup_x2();
+ dup_x2();
+ pop();
+ } else {
+ // p -> po -> opo -> oop -> o
+ dup_x1();
+ swap();
+ }
+ invoke_constructor(boxed, new Signature(Constants.CONSTRUCTOR_NAME, Type.VOID_TYPE, new Type[]{ type }));
+ }
+ }
+ }
+
+ /**
+ * If the argument is a primitive class, replaces the object
+ * on the top of the stack with the unwrapped (primitive)
+ * equivalent. For example, Character -> char.
+ * @param type the class indicating the desired type of the top stack value
+ * @return true if the value was unboxed
+ */
+ public void unbox(Type type) {
+ Type t = Constants.TYPE_NUMBER;
+ Signature sig = null;
+ switch (type.getSort()) {
+ case Type.VOID:
+ return;
+ case Type.CHAR:
+ t = Constants.TYPE_CHARACTER;
+ sig = CHAR_VALUE;
+ break;
+ case Type.BOOLEAN:
+ t = Constants.TYPE_BOOLEAN;
+ sig = BOOLEAN_VALUE;
+ break;
+ case Type.DOUBLE:
+ sig = DOUBLE_VALUE;
+ break;
+ case Type.FLOAT:
+ sig = FLOAT_VALUE;
+ break;
+ case Type.LONG:
+ sig = LONG_VALUE;
+ break;
+ case Type.INT:
+ case Type.SHORT:
+ case Type.BYTE:
+ sig = INT_VALUE;
+ }
+
+ if (sig == null) {
+ checkcast(type);
+ } else {
+ checkcast(t);
+ invoke_virtual(t, sig);
+ }
+ }
+
+ /**
+ * Allocates and fills an Object[] array with the arguments to the
+ * current method. Primitive values are inserted as their boxed
+ * (Object) equivalents.
+ */
+ public void create_arg_array() {
+ /* generates:
+ Object[] args = new Object[]{ arg1, new Integer(arg2) };
+ */
+
+ push(state.argumentTypes.length);
+ newarray();
+ for (int i = 0; i < state.argumentTypes.length; i++) {
+ dup();
+ push(i);
+ load_arg(i);
+ box(state.argumentTypes[i]);
+ aastore();
+ }
+ }
+
+
+ /**
+ * Pushes a zero onto the stack if the argument is a primitive class, or a null otherwise.
+ */
+ public void zero_or_null(Type type) {
+ if (TypeUtils.isPrimitive(type)) {
+ switch (type.getSort()) {
+ case Type.DOUBLE:
+ push(0d);
+ break;
+ case Type.LONG:
+ push(0L);
+ break;
+ case Type.FLOAT:
+ push(0f);
+ break;
+ case Type.VOID:
+ aconst_null();
+ default:
+ push(0);
+ }
+ } else {
+ aconst_null();
+ }
+ }
+
+ /**
+ * Unboxes the object on the top of the stack. If the object is null, the
+ * unboxed primitive value becomes zero.
+ */
+ public void unbox_or_zero(Type type) {
+ if (TypeUtils.isPrimitive(type)) {
+ if (type != Type.VOID_TYPE) {
+ Label nonNull = make_label();
+ Label end = make_label();
+ dup();
+ ifnonnull(nonNull);
+ pop();
+ zero_or_null(type);
+ goTo(end);
+ mark(nonNull);
+ unbox(type);
+ mark(end);
+ }
+ } else {
+ checkcast(type);
+ }
+ }
+
+ public void visitMaxs(int maxStack, int maxLocals) {
+ if (!TypeUtils.isAbstract(state.access)) {
+ mv.visitMaxs(0, 0);
+ }
+ }
+
+ public void invoke(MethodInfo method, Type virtualType) {
+ ClassInfo classInfo = method.getClassInfo();
+ Type type = classInfo.getType();
+ Signature sig = method.getSignature();
+ if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) {
+ invoke_constructor(type, sig);
+ } else if (TypeUtils.isInterface(classInfo.getModifiers())) {
+ invoke_interface(type, sig);
+ } else if (TypeUtils.isStatic(method.getModifiers())) {
+ invoke_static(type, sig);
+ } else {
+ invoke_virtual(virtualType, sig);
+ }
+ }
+
+ public void invoke(MethodInfo method) {
+ invoke(method, method.getClassInfo().getType());
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/CodeGenerationException.java b/cglib-and-asm/src/org/mockito/cglib/core/CodeGenerationException.java
new file mode 100644
index 0000000..2fb46ff
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/CodeGenerationException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+/**
+ * @version $Id: CodeGenerationException.java,v 1.3 2004/06/24 21:15:21 herbyderby Exp $
+ */
+public class CodeGenerationException extends RuntimeException {
+ private Throwable cause;
+
+ public CodeGenerationException(Throwable cause) {
+ super(cause.getClass().getName() + "-->" + cause.getMessage());
+ this.cause = cause;
+ }
+
+ public Throwable getCause() {
+ return cause;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/CollectionUtils.java b/cglib-and-asm/src/org/mockito/cglib/core/CollectionUtils.java
new file mode 100644
index 0000000..97eeabe
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/CollectionUtils.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.util.*;
+import java.lang.reflect.Array;
+
+/**
+ * @author Chris Nokleberg
+ * @version $Id: CollectionUtils.java,v 1.7 2004/06/24 21:15:21 herbyderby Exp $
+ */
+public class CollectionUtils {
+ private CollectionUtils() { }
+
+ public static Map bucket(Collection c, Transformer t) {
+ Map buckets = new HashMap();
+ for (Iterator it = c.iterator(); it.hasNext();) {
+ Object value = (Object)it.next();
+ Object key = t.transform(value);
+ List bucket = (List)buckets.get(key);
+ if (bucket == null) {
+ buckets.put(key, bucket = new LinkedList());
+ }
+ bucket.add(value);
+ }
+ return buckets;
+ }
+
+ public static void reverse(Map source, Map target) {
+ for (Iterator it = source.keySet().iterator(); it.hasNext();) {
+ Object key = it.next();
+ target.put(source.get(key), key);
+ }
+ }
+
+ public static Collection filter(Collection c, Predicate p) {
+ Iterator it = c.iterator();
+ while (it.hasNext()) {
+ if (!p.evaluate(it.next())) {
+ it.remove();
+ }
+ }
+ return c;
+ }
+
+ public static List transform(Collection c, Transformer t) {
+ List result = new ArrayList(c.size());
+ for (Iterator it = c.iterator(); it.hasNext();) {
+ result.add(t.transform(it.next()));
+ }
+ return result;
+ }
+
+ public static Map getIndexMap(List list) {
+ Map indexes = new HashMap();
+ int index = 0;
+ for (Iterator it = list.iterator(); it.hasNext();) {
+ indexes.put(it.next(), new Integer(index++));
+ }
+ return indexes;
+ }
+}
+
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/Constants.java b/cglib-and-asm/src/org/mockito/cglib/core/Constants.java
new file mode 100644
index 0000000..2336b2c
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/Constants.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.Type;
+
+/**
+ * @author Juozas Baliuka <a href="mailto:baliuka@mwm.lt">baliuka@mwm.lt</a>
+ * @version $Id: Constants.java,v 1.21 2006/03/05 02:43:19 herbyderby Exp $
+ */
+public interface Constants extends org.mockito.asm.Opcodes {
+ public static final Class[] EMPTY_CLASS_ARRAY = {};
+ public static final Type[] TYPES_EMPTY = {};
+
+ public static final Signature SIG_STATIC =
+ TypeUtils.parseSignature("void <clinit>()");
+
+ public static final Type TYPE_OBJECT_ARRAY = TypeUtils.parseType("Object[]");
+ public static final Type TYPE_CLASS_ARRAY = TypeUtils.parseType("Class[]");
+ public static final Type TYPE_STRING_ARRAY = TypeUtils.parseType("String[]");
+
+ public static final Type TYPE_OBJECT = TypeUtils.parseType("Object");
+ public static final Type TYPE_CLASS = TypeUtils.parseType("Class");
+ public static final Type TYPE_CLASS_LOADER = TypeUtils.parseType("ClassLoader");
+ public static final Type TYPE_CHARACTER = TypeUtils.parseType("Character");
+ public static final Type TYPE_BOOLEAN = TypeUtils.parseType("Boolean");
+ public static final Type TYPE_DOUBLE = TypeUtils.parseType("Double");
+ public static final Type TYPE_FLOAT = TypeUtils.parseType("Float");
+ public static final Type TYPE_LONG = TypeUtils.parseType("Long");
+ public static final Type TYPE_INTEGER = TypeUtils.parseType("Integer");
+ public static final Type TYPE_SHORT = TypeUtils.parseType("Short");
+ public static final Type TYPE_BYTE = TypeUtils.parseType("Byte");
+ public static final Type TYPE_NUMBER = TypeUtils.parseType("Number");
+ public static final Type TYPE_STRING = TypeUtils.parseType("String");
+ public static final Type TYPE_THROWABLE = TypeUtils.parseType("Throwable");
+ public static final Type TYPE_BIG_INTEGER = TypeUtils.parseType("java.math.BigInteger");
+ public static final Type TYPE_BIG_DECIMAL = TypeUtils.parseType("java.math.BigDecimal");
+ public static final Type TYPE_STRING_BUFFER = TypeUtils.parseType("StringBuffer");
+ public static final Type TYPE_RUNTIME_EXCEPTION = TypeUtils.parseType("RuntimeException");
+ public static final Type TYPE_ERROR = TypeUtils.parseType("Error");
+ public static final Type TYPE_SYSTEM = TypeUtils.parseType("System");
+ public static final Type TYPE_SIGNATURE = TypeUtils.parseType("org.mockito.cglib.core.Signature");
+
+ public static final String CONSTRUCTOR_NAME = "<init>";
+ public static final String STATIC_NAME = "<clinit>";
+ public static final String SOURCE_FILE = "<generated>";
+ public static final String SUID_FIELD_NAME = "serialVersionUID";
+
+ public static final int PRIVATE_FINAL_STATIC = ACC_PRIVATE | ACC_FINAL | ACC_STATIC;
+
+ public static final int SWITCH_STYLE_TRIE = 0;
+ public static final int SWITCH_STYLE_HASH = 1;
+ public static final int SWITCH_STYLE_HASHONLY = 2;
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/Converter.java b/cglib-and-asm/src/org/mockito/cglib/core/Converter.java
new file mode 100644
index 0000000..60c9687
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/Converter.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+public interface Converter {
+ Object convert(Object value, Class target, Object context);
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/Customizer.java b/cglib-and-asm/src/org/mockito/cglib/core/Customizer.java
new file mode 100644
index 0000000..15beba7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/Customizer.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.Type;
+
+public interface Customizer {
+ void customize(CodeEmitter e, Type type);
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/DebuggingClassWriter.java b/cglib-and-asm/src/org/mockito/cglib/core/DebuggingClassWriter.java
new file mode 100644
index 0000000..7c16440
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/DebuggingClassWriter.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.ClassReader;
+import org.mockito.asm.ClassWriter;
+import org.mockito.asm.util.TraceClassVisitor;
+
+import java.io.*;
+
+public class DebuggingClassWriter extends ClassWriter {
+
+ public static final String DEBUG_LOCATION_PROPERTY = "cglib.debugLocation";
+
+ private static String debugLocation;
+ private static boolean traceEnabled;
+
+ private String className;
+ private String superName;
+
+ static {
+ debugLocation = System.getProperty(DEBUG_LOCATION_PROPERTY);
+ if (debugLocation != null) {
+ System.err.println("CGLIB debugging enabled, writing to '" + debugLocation + "'");
+ try {
+ Class.forName("org.mockito.asm.util.TraceClassVisitor");
+ traceEnabled = true;
+ } catch (Throwable ignore) {
+ }
+ }
+ }
+
+ public DebuggingClassWriter(int flags) {
+ super(flags);
+ }
+
+ public void visit(int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ className = name.replace('/', '.');
+ this.superName = superName.replace('/', '.');
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public String getSuperName() {
+ return superName;
+ }
+
+ public byte[] toByteArray() {
+
+ return (byte[]) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+
+
+ byte[] b = DebuggingClassWriter.super.toByteArray();
+ if (debugLocation != null) {
+ String dirs = className.replace('.', File.separatorChar);
+ try {
+ new File(debugLocation + File.separatorChar + dirs).getParentFile().mkdirs();
+
+ File file = new File(new File(debugLocation), dirs + ".class");
+ OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
+ try {
+ out.write(b);
+ } finally {
+ out.close();
+ }
+
+ if (traceEnabled) {
+ file = new File(new File(debugLocation), dirs + ".asm");
+ out = new BufferedOutputStream(new FileOutputStream(file));
+ try {
+ ClassReader cr = new ClassReader(b);
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));
+ TraceClassVisitor tcv = new TraceClassVisitor(null, pw);
+ cr.accept(tcv, 0);
+ pw.flush();
+ } finally {
+ out.close();
+ }
+ }
+ } catch (IOException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+ return b;
+ }
+ });
+
+ }
+ }
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/DefaultGeneratorStrategy.java b/cglib-and-asm/src/org/mockito/cglib/core/DefaultGeneratorStrategy.java
new file mode 100644
index 0000000..55b52d8
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/DefaultGeneratorStrategy.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.ClassWriter;
+
+public class DefaultGeneratorStrategy implements GeneratorStrategy {
+ public static final DefaultGeneratorStrategy INSTANCE = new DefaultGeneratorStrategy();
+
+ public byte[] generate(ClassGenerator cg) throws Exception {
+ ClassWriter cw = getClassWriter();
+ transform(cg).generateClass(cw);
+ return transform(cw.toByteArray());
+ }
+
+ protected ClassWriter getClassWriter() throws Exception {
+ return new DebuggingClassWriter(ClassWriter.COMPUTE_MAXS);
+ }
+
+ protected byte[] transform(byte[] b) throws Exception {
+ return b;
+ }
+
+ protected ClassGenerator transform(ClassGenerator cg) throws Exception {
+ return cg;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/DefaultNamingPolicy.java b/cglib-and-asm/src/org/mockito/cglib/core/DefaultNamingPolicy.java
new file mode 100644
index 0000000..a13f542
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/DefaultNamingPolicy.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.util.Set;
+
+/**
+ * The default policy used by {@link AbstractClassGenerator}.
+ * Generates names such as
+ * <p><code>org.mockito.cglib.Foo$$EnhancerByCGLIB$$38272841</code><p>
+ * This is composed of a prefix based on the name of the superclass, a fixed
+ * string incorporating the CGLIB class responsible for generation, and a
+ * hashcode derived from the parameters used to create the object. If the same
+ * name has been previously been used in the same <code>ClassLoader</code>, a
+ * suffix is added to ensure uniqueness.
+ */
+public class DefaultNamingPolicy implements NamingPolicy {
+ public static final DefaultNamingPolicy INSTANCE = new DefaultNamingPolicy();
+
+ public String getClassName(String prefix, String source, Object key, Predicate names) {
+ if (prefix == null) {
+ prefix = "org.mockito.cglib.empty.Object";
+ } else if (prefix.startsWith("java")) {
+ prefix = "$" + prefix;
+ }
+ String base =
+ prefix + "$$" +
+ source.substring(source.lastIndexOf('.') + 1) +
+ getTag() + "$$" +
+ Integer.toHexString(key.hashCode());
+ String attempt = base;
+ int index = 2;
+ while (names.evaluate(attempt))
+ attempt = base + "_" + index++;
+ return attempt;
+ }
+
+ /**
+ * Returns a string which is incorporated into every generated class name.
+ * By default returns "ByCGLIB"
+ */
+ protected String getTag() {
+ return "ByCGLIB";
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/DuplicatesPredicate.java b/cglib-and-asm/src/org/mockito/cglib/core/DuplicatesPredicate.java
new file mode 100644
index 0000000..ad46239
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/DuplicatesPredicate.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+public class DuplicatesPredicate implements Predicate {
+ private Set unique = new HashSet();
+
+ public boolean evaluate(Object arg) {
+ return unique.add(MethodWrapper.create((Method)arg));
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/EmitUtils.java b/cglib-and-asm/src/org/mockito/cglib/core/EmitUtils.java
new file mode 100644
index 0000000..9cb67ad
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/EmitUtils.java
@@ -0,0 +1,918 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+import org.mockito.asm.Label;
+import org.mockito.asm.Type;
+
+public class EmitUtils {
+ private static final Signature CSTRUCT_NULL =
+ TypeUtils.parseConstructor("");
+ private static final Signature CSTRUCT_THROWABLE =
+ TypeUtils.parseConstructor("Throwable");
+
+ private static final Signature GET_NAME =
+ TypeUtils.parseSignature("String getName()");
+ private static final Signature HASH_CODE =
+ TypeUtils.parseSignature("int hashCode()");
+ private static final Signature EQUALS =
+ TypeUtils.parseSignature("boolean equals(Object)");
+ private static final Signature STRING_LENGTH =
+ TypeUtils.parseSignature("int length()");
+ private static final Signature STRING_CHAR_AT =
+ TypeUtils.parseSignature("char charAt(int)");
+ private static final Signature FOR_NAME =
+ TypeUtils.parseSignature("Class forName(String)");
+ private static final Signature DOUBLE_TO_LONG_BITS =
+ TypeUtils.parseSignature("long doubleToLongBits(double)");
+ private static final Signature FLOAT_TO_INT_BITS =
+ TypeUtils.parseSignature("int floatToIntBits(float)");
+ private static final Signature TO_STRING =
+ TypeUtils.parseSignature("String toString()");
+ private static final Signature APPEND_STRING =
+ TypeUtils.parseSignature("StringBuffer append(String)");
+ private static final Signature APPEND_INT =
+ TypeUtils.parseSignature("StringBuffer append(int)");
+ private static final Signature APPEND_DOUBLE =
+ TypeUtils.parseSignature("StringBuffer append(double)");
+ private static final Signature APPEND_FLOAT =
+ TypeUtils.parseSignature("StringBuffer append(float)");
+ private static final Signature APPEND_CHAR =
+ TypeUtils.parseSignature("StringBuffer append(char)");
+ private static final Signature APPEND_LONG =
+ TypeUtils.parseSignature("StringBuffer append(long)");
+ private static final Signature APPEND_BOOLEAN =
+ TypeUtils.parseSignature("StringBuffer append(boolean)");
+ private static final Signature LENGTH =
+ TypeUtils.parseSignature("int length()");
+ private static final Signature SET_LENGTH =
+ TypeUtils.parseSignature("void setLength(int)");
+ private static final Signature GET_DECLARED_METHOD =
+ TypeUtils.parseSignature("java.lang.reflect.Method getDeclaredMethod(String, Class[])");
+
+
+
+ public static final ArrayDelimiters DEFAULT_DELIMITERS = new ArrayDelimiters("{", ", ", "}");
+
+ private EmitUtils() {
+ }
+
+ public static void factory_method(ClassEmitter ce, Signature sig) {
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, sig, null);
+ e.new_instance_this();
+ e.dup();
+ e.load_args();
+ e.invoke_constructor_this(TypeUtils.parseConstructor(sig.getArgumentTypes()));
+ e.return_value();
+ e.end_method();
+ }
+
+ public static void null_constructor(ClassEmitter ce) {
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_NULL, null);
+ e.load_this();
+ e.super_invoke_constructor();
+ e.return_value();
+ e.end_method();
+ }
+
+ /**
+ * Process an array on the stack. Assumes the top item on the stack
+ * is an array of the specified type. For each element in the array,
+ * puts the element on the stack and triggers the callback.
+ * @param type the type of the array (type.isArray() must be true)
+ * @param callback the callback triggered for each element
+ */
+ public static void process_array(CodeEmitter e, Type type, ProcessArrayCallback callback) {
+ Type componentType = TypeUtils.getComponentType(type);
+ Local array = e.make_local();
+ Local loopvar = e.make_local(Type.INT_TYPE);
+ Label loopbody = e.make_label();
+ Label checkloop = e.make_label();
+ e.store_local(array);
+ e.push(0);
+ e.store_local(loopvar);
+ e.goTo(checkloop);
+
+ e.mark(loopbody);
+ e.load_local(array);
+ e.load_local(loopvar);
+ e.array_load(componentType);
+ callback.processElement(componentType);
+ e.iinc(loopvar, 1);
+
+ e.mark(checkloop);
+ e.load_local(loopvar);
+ e.load_local(array);
+ e.arraylength();
+ e.if_icmp(e.LT, loopbody);
+ }
+
+ /**
+ * Process two arrays on the stack in parallel. Assumes the top two items on the stack
+ * are arrays of the specified class. The arrays must be the same length. For each pair
+ * of elements in the arrays, puts the pair on the stack and triggers the callback.
+ * @param type the type of the arrays (type.isArray() must be true)
+ * @param callback the callback triggered for each pair of elements
+ */
+ public static void process_arrays(CodeEmitter e, Type type, ProcessArrayCallback callback) {
+ Type componentType = TypeUtils.getComponentType(type);
+ Local array1 = e.make_local();
+ Local array2 = e.make_local();
+ Local loopvar = e.make_local(Type.INT_TYPE);
+ Label loopbody = e.make_label();
+ Label checkloop = e.make_label();
+ e.store_local(array1);
+ e.store_local(array2);
+ e.push(0);
+ e.store_local(loopvar);
+ e.goTo(checkloop);
+
+ e.mark(loopbody);
+ e.load_local(array1);
+ e.load_local(loopvar);
+ e.array_load(componentType);
+ e.load_local(array2);
+ e.load_local(loopvar);
+ e.array_load(componentType);
+ callback.processElement(componentType);
+ e.iinc(loopvar, 1);
+
+ e.mark(checkloop);
+ e.load_local(loopvar);
+ e.load_local(array1);
+ e.arraylength();
+ e.if_icmp(e.LT, loopbody);
+ }
+
+ public static void string_switch(CodeEmitter e, String[] strings, int switchStyle, ObjectSwitchCallback callback) {
+ try {
+ switch (switchStyle) {
+ case Constants.SWITCH_STYLE_TRIE:
+ string_switch_trie(e, strings, callback);
+ break;
+ case Constants.SWITCH_STYLE_HASH:
+ string_switch_hash(e, strings, callback, false);
+ break;
+ case Constants.SWITCH_STYLE_HASHONLY:
+ string_switch_hash(e, strings, callback, true);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown switch style " + switchStyle);
+ }
+ } catch (RuntimeException ex) {
+ throw ex;
+ } catch (Error ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new CodeGenerationException(ex);
+ }
+ }
+
+ private static void string_switch_trie(final CodeEmitter e,
+ String[] strings,
+ final ObjectSwitchCallback callback) throws Exception {
+ final Label def = e.make_label();
+ final Label end = e.make_label();
+ final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() {
+ public Object transform(Object value) {
+ return new Integer(((String)value).length());
+ }
+ });
+ e.dup();
+ e.invoke_virtual(Constants.TYPE_STRING, STRING_LENGTH);
+ e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
+ public void processCase(int key, Label ignore_end) throws Exception {
+ List bucket = (List)buckets.get(new Integer(key));
+ stringSwitchHelper(e, bucket, callback, def, end, 0);
+ }
+ public void processDefault() {
+ e.goTo(def);
+ }
+ });
+ e.mark(def);
+ e.pop();
+ callback.processDefault();
+ e.mark(end);
+ }
+
+ private static void stringSwitchHelper(final CodeEmitter e,
+ List strings,
+ final ObjectSwitchCallback callback,
+ final Label def,
+ final Label end,
+ final int index) throws Exception {
+ final int len = ((String)strings.get(0)).length();
+ final Map buckets = CollectionUtils.bucket(strings, new Transformer() {
+ public Object transform(Object value) {
+ return new Integer(((String)value).charAt(index));
+ }
+ });
+ e.dup();
+ e.push(index);
+ e.invoke_virtual(Constants.TYPE_STRING, STRING_CHAR_AT);
+ e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
+ public void processCase(int key, Label ignore_end) throws Exception {
+ List bucket = (List)buckets.get(new Integer(key));
+ if (index + 1 == len) {
+ e.pop();
+ callback.processCase(bucket.get(0), end);
+ } else {
+ stringSwitchHelper(e, bucket, callback, def, end, index + 1);
+ }
+ }
+ public void processDefault() {
+ e.goTo(def);
+ }
+ });
+ }
+
+ static int[] getSwitchKeys(Map buckets) {
+ int[] keys = new int[buckets.size()];
+ int index = 0;
+ for (Iterator it = buckets.keySet().iterator(); it.hasNext();) {
+ keys[index++] = ((Integer)it.next()).intValue();
+ }
+ Arrays.sort(keys);
+ return keys;
+ }
+
+ private static void string_switch_hash(final CodeEmitter e,
+ final String[] strings,
+ final ObjectSwitchCallback callback,
+ final boolean skipEquals) throws Exception {
+ final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() {
+ public Object transform(Object value) {
+ return new Integer(value.hashCode());
+ }
+ });
+ final Label def = e.make_label();
+ final Label end = e.make_label();
+ e.dup();
+ e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE);
+ e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
+ public void processCase(int key, Label ignore_end) throws Exception {
+ List bucket = (List)buckets.get(new Integer(key));
+ Label next = null;
+ if (skipEquals && bucket.size() == 1) {
+ if (skipEquals)
+ e.pop();
+ callback.processCase((String)bucket.get(0), end);
+ } else {
+ for (Iterator it = bucket.iterator(); it.hasNext();) {
+ String string = (String)it.next();
+ if (next != null) {
+ e.mark(next);
+ }
+ if (it.hasNext()) {
+ e.dup();
+ }
+ e.push(string);
+ e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
+ if (it.hasNext()) {
+ e.if_jump(e.EQ, next = e.make_label());
+ e.pop();
+ } else {
+ e.if_jump(e.EQ, def);
+ }
+ callback.processCase(string, end);
+ }
+ }
+ }
+ public void processDefault() {
+ e.pop();
+ }
+ });
+ e.mark(def);
+ callback.processDefault();
+ e.mark(end);
+ }
+
+ public static void load_class_this(CodeEmitter e) {
+ load_class_helper(e, e.getClassEmitter().getClassType());
+ }
+
+ public static void load_class(CodeEmitter e, Type type) {
+ if (TypeUtils.isPrimitive(type)) {
+ if (type == Type.VOID_TYPE) {
+ throw new IllegalArgumentException("cannot load void type");
+ }
+ e.getstatic(TypeUtils.getBoxedType(type), "TYPE", Constants.TYPE_CLASS);
+ } else {
+ load_class_helper(e, type);
+ }
+ }
+
+ private static void load_class_helper(CodeEmitter e, final Type type) {
+ if (e.isStaticHook()) {
+ // have to fall back on non-optimized load
+ e.push(TypeUtils.emulateClassGetName(type));
+ e.invoke_static(Constants.TYPE_CLASS, FOR_NAME);
+ } else {
+ ClassEmitter ce = e.getClassEmitter();
+ String typeName = TypeUtils.emulateClassGetName(type);
+
+ // TODO: can end up with duplicated field names when using chained transformers; incorporate static hook # somehow
+ String fieldName = "CGLIB$load_class$" + TypeUtils.escapeType(typeName);
+ if (!ce.isFieldDeclared(fieldName)) {
+ ce.declare_field(Constants.PRIVATE_FINAL_STATIC, fieldName, Constants.TYPE_CLASS, null);
+ CodeEmitter hook = ce.getStaticHook();
+ hook.push(typeName);
+ hook.invoke_static(Constants.TYPE_CLASS, FOR_NAME);
+ hook.putstatic(ce.getClassType(), fieldName, Constants.TYPE_CLASS);
+ }
+ e.getfield(fieldName);
+ }
+ }
+
+ public static void push_array(CodeEmitter e, Object[] array) {
+ e.push(array.length);
+ e.newarray(Type.getType(remapComponentType(array.getClass().getComponentType())));
+ for (int i = 0; i < array.length; i++) {
+ e.dup();
+ e.push(i);
+ push_object(e, array[i]);
+ e.aastore();
+ }
+ }
+
+ private static Class remapComponentType(Class componentType) {
+ if (componentType.equals(Type.class))
+ return Class.class;
+ return componentType;
+ }
+
+ public static void push_object(CodeEmitter e, Object obj) {
+ if (obj == null) {
+ e.aconst_null();
+ } else {
+ Class type = obj.getClass();
+ if (type.isArray()) {
+ push_array(e, (Object[])obj);
+ } else if (obj instanceof String) {
+ e.push((String)obj);
+ } else if (obj instanceof Type) {
+ load_class(e, (Type)obj);
+ } else if (obj instanceof Class) {
+ load_class(e, Type.getType((Class)obj));
+ } else if (obj instanceof BigInteger) {
+ e.new_instance(Constants.TYPE_BIG_INTEGER);
+ e.dup();
+ e.push(obj.toString());
+ e.invoke_constructor(Constants.TYPE_BIG_INTEGER);
+ } else if (obj instanceof BigDecimal) {
+ e.new_instance(Constants.TYPE_BIG_DECIMAL);
+ e.dup();
+ e.push(obj.toString());
+ e.invoke_constructor(Constants.TYPE_BIG_DECIMAL);
+ } else {
+ throw new IllegalArgumentException("unknown type: " + obj.getClass());
+ }
+ }
+ }
+
+ public static void hash_code(CodeEmitter e, Type type, int multiplier, Customizer customizer) {
+ if (TypeUtils.isArray(type)) {
+ hash_array(e, type, multiplier, customizer);
+ } else {
+ e.swap(Type.INT_TYPE, type);
+ e.push(multiplier);
+ e.math(e.MUL, Type.INT_TYPE);
+ e.swap(type, Type.INT_TYPE);
+ if (TypeUtils.isPrimitive(type)) {
+ hash_primitive(e, type);
+ } else {
+ hash_object(e, type, customizer);
+ }
+ e.math(e.ADD, Type.INT_TYPE);
+ }
+ }
+
+ private static void hash_array(final CodeEmitter e, Type type, final int multiplier, final Customizer customizer) {
+ Label skip = e.make_label();
+ Label end = e.make_label();
+ e.dup();
+ e.ifnull(skip);
+ EmitUtils.process_array(e, type, new ProcessArrayCallback() {
+ public void processElement(Type type) {
+ hash_code(e, type, multiplier, customizer);
+ }
+ });
+ e.goTo(end);
+ e.mark(skip);
+ e.pop();
+ e.mark(end);
+ }
+
+ private static void hash_object(CodeEmitter e, Type type, Customizer customizer) {
+ // (f == null) ? 0 : f.hashCode();
+ Label skip = e.make_label();
+ Label end = e.make_label();
+ e.dup();
+ e.ifnull(skip);
+ if (customizer != null) {
+ customizer.customize(e, type);
+ }
+ e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE);
+ e.goTo(end);
+ e.mark(skip);
+ e.pop();
+ e.push(0);
+ e.mark(end);
+ }
+
+ private static void hash_primitive(CodeEmitter e, Type type) {
+ switch (type.getSort()) {
+ case Type.BOOLEAN:
+ // f ? 0 : 1
+ e.push(1);
+ e.math(e.XOR, Type.INT_TYPE);
+ break;
+ case Type.FLOAT:
+ // Float.floatToIntBits(f)
+ e.invoke_static(Constants.TYPE_FLOAT, FLOAT_TO_INT_BITS);
+ break;
+ case Type.DOUBLE:
+ // Double.doubleToLongBits(f), hash_code(Long.TYPE)
+ e.invoke_static(Constants.TYPE_DOUBLE, DOUBLE_TO_LONG_BITS);
+ // fall through
+ case Type.LONG:
+ hash_long(e);
+ }
+ }
+
+ private static void hash_long(CodeEmitter e) {
+ // (int)(f ^ (f >>> 32))
+ e.dup2();
+ e.push(32);
+ e.math(e.USHR, Type.LONG_TYPE);
+ e.math(e.XOR, Type.LONG_TYPE);
+ e.cast_numeric(Type.LONG_TYPE, Type.INT_TYPE);
+ }
+
+// public static void not_equals(CodeEmitter e, Type type, Label notEquals) {
+// not_equals(e, type, notEquals, null);
+// }
+
+ /**
+ * Branches to the specified label if the top two items on the stack
+ * are not equal. The items must both be of the specified
+ * class. Equality is determined by comparing primitive values
+ * directly and by invoking the <code>equals</code> method for
+ * Objects. Arrays are recursively processed in the same manner.
+ */
+ public static void not_equals(final CodeEmitter e, Type type, final Label notEquals, final Customizer customizer) {
+ (new ProcessArrayCallback() {
+ public void processElement(Type type) {
+ not_equals_helper(e, type, notEquals, customizer, this);
+ }
+ }).processElement(type);
+ }
+
+ private static void not_equals_helper(CodeEmitter e,
+ Type type,
+ Label notEquals,
+ Customizer customizer,
+ ProcessArrayCallback callback) {
+ if (TypeUtils.isPrimitive(type)) {
+ e.if_cmp(type, e.NE, notEquals);
+ } else {
+ Label end = e.make_label();
+ nullcmp(e, notEquals, end);
+ if (TypeUtils.isArray(type)) {
+ Label checkContents = e.make_label();
+ e.dup2();
+ e.arraylength();
+ e.swap();
+ e.arraylength();
+ e.if_icmp(e.EQ, checkContents);
+ e.pop2();
+ e.goTo(notEquals);
+ e.mark(checkContents);
+ EmitUtils.process_arrays(e, type, callback);
+ } else {
+ if (customizer != null) {
+ customizer.customize(e, type);
+ e.swap();
+ customizer.customize(e, type);
+ }
+ e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
+ e.if_jump(e.EQ, notEquals);
+ }
+ e.mark(end);
+ }
+ }
+
+ /**
+ * If both objects on the top of the stack are non-null, does nothing.
+ * If one is null, or both are null, both are popped off and execution
+ * branches to the respective label.
+ * @param oneNull label to branch to if only one of the objects is null
+ * @param bothNull label to branch to if both of the objects are null
+ */
+ private static void nullcmp(CodeEmitter e, Label oneNull, Label bothNull) {
+ e.dup2();
+ Label nonNull = e.make_label();
+ Label oneNullHelper = e.make_label();
+ Label end = e.make_label();
+ e.ifnonnull(nonNull);
+ e.ifnonnull(oneNullHelper);
+ e.pop2();
+ e.goTo(bothNull);
+
+ e.mark(nonNull);
+ e.ifnull(oneNullHelper);
+ e.goTo(end);
+
+ e.mark(oneNullHelper);
+ e.pop2();
+ e.goTo(oneNull);
+
+ e.mark(end);
+ }
+
+ /*
+ public static void to_string(CodeEmitter e,
+ Type type,
+ ArrayDelimiters delims,
+ Customizer customizer) {
+ e.new_instance(Constants.TYPE_STRING_BUFFER);
+ e.dup();
+ e.invoke_constructor(Constants.TYPE_STRING_BUFFER);
+ e.swap();
+ append_string(e, type, delims, customizer);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING);
+ }
+ */
+
+ public static void append_string(final CodeEmitter e,
+ Type type,
+ final ArrayDelimiters delims,
+ final Customizer customizer) {
+ final ArrayDelimiters d = (delims != null) ? delims : DEFAULT_DELIMITERS;
+ ProcessArrayCallback callback = new ProcessArrayCallback() {
+ public void processElement(Type type) {
+ append_string_helper(e, type, d, customizer, this);
+ e.push(d.inside);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ }
+ };
+ append_string_helper(e, type, d, customizer, callback);
+ }
+
+ private static void append_string_helper(CodeEmitter e,
+ Type type,
+ ArrayDelimiters delims,
+ Customizer customizer,
+ ProcessArrayCallback callback) {
+ Label skip = e.make_label();
+ Label end = e.make_label();
+ if (TypeUtils.isPrimitive(type)) {
+ switch (type.getSort()) {
+ case Type.INT:
+ case Type.SHORT:
+ case Type.BYTE:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_INT);
+ break;
+ case Type.DOUBLE:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_DOUBLE);
+ break;
+ case Type.FLOAT:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_FLOAT);
+ break;
+ case Type.LONG:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_LONG);
+ break;
+ case Type.BOOLEAN:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_BOOLEAN);
+ break;
+ case Type.CHAR:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_CHAR);
+ break;
+ }
+ } else if (TypeUtils.isArray(type)) {
+ e.dup();
+ e.ifnull(skip);
+ e.swap();
+ if (delims != null && delims.before != null && !"".equals(delims.before)) {
+ e.push(delims.before);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ e.swap();
+ }
+ EmitUtils.process_array(e, type, callback);
+ shrinkStringBuffer(e, 2);
+ if (delims != null && delims.after != null && !"".equals(delims.after)) {
+ e.push(delims.after);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ }
+ } else {
+ e.dup();
+ e.ifnull(skip);
+ if (customizer != null) {
+ customizer.customize(e, type);
+ }
+ e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ }
+ e.goTo(end);
+ e.mark(skip);
+ e.pop();
+ e.push("null");
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ e.mark(end);
+ }
+
+ private static void shrinkStringBuffer(CodeEmitter e, int amt) {
+ e.dup();
+ e.dup();
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, LENGTH);
+ e.push(amt);
+ e.math(e.SUB, Type.INT_TYPE);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, SET_LENGTH);
+ }
+
+ public static class ArrayDelimiters {
+ private String before;
+ private String inside;
+ private String after;
+
+ public ArrayDelimiters(String before, String inside, String after) {
+ this.before = before;
+ this.inside = inside;
+ this.after = after;
+ }
+ }
+
+ public static void load_method(CodeEmitter e, MethodInfo method) {
+ load_class(e, method.getClassInfo().getType());
+ e.push(method.getSignature().getName());
+ push_object(e, method.getSignature().getArgumentTypes());
+ e.invoke_virtual(Constants.TYPE_CLASS, GET_DECLARED_METHOD);
+ }
+
+ private interface ParameterTyper {
+ Type[] getParameterTypes(MethodInfo member);
+ }
+
+ public static void method_switch(CodeEmitter e,
+ List methods,
+ ObjectSwitchCallback callback) {
+ member_switch_helper(e, methods, callback, true);
+ }
+
+ public static void constructor_switch(CodeEmitter e,
+ List constructors,
+ ObjectSwitchCallback callback) {
+ member_switch_helper(e, constructors, callback, false);
+ }
+
+ private static void member_switch_helper(final CodeEmitter e,
+ List members,
+ final ObjectSwitchCallback callback,
+ boolean useName) {
+ try {
+ final Map cache = new HashMap();
+ final ParameterTyper cached = new ParameterTyper() {
+ public Type[] getParameterTypes(MethodInfo member) {
+ Type[] types = (Type[])cache.get(member);
+ if (types == null) {
+ cache.put(member, types = member.getSignature().getArgumentTypes());
+ }
+ return types;
+ }
+ };
+ final Label def = e.make_label();
+ final Label end = e.make_label();
+ if (useName) {
+ e.swap();
+ final Map buckets = CollectionUtils.bucket(members, new Transformer() {
+ public Object transform(Object value) {
+ return ((MethodInfo)value).getSignature().getName();
+ }
+ });
+ String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]);
+ EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label dontUseEnd) throws Exception {
+ member_helper_size(e, (List)buckets.get(key), callback, cached, def, end);
+ }
+ public void processDefault() throws Exception {
+ e.goTo(def);
+ }
+ });
+ } else {
+ member_helper_size(e, members, callback, cached, def, end);
+ }
+ e.mark(def);
+ e.pop();
+ callback.processDefault();
+ e.mark(end);
+ } catch (RuntimeException ex) {
+ throw ex;
+ } catch (Error ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new CodeGenerationException(ex);
+ }
+ }
+
+ private static void member_helper_size(final CodeEmitter e,
+ List members,
+ final ObjectSwitchCallback callback,
+ final ParameterTyper typer,
+ final Label def,
+ final Label end) throws Exception {
+ final Map buckets = CollectionUtils.bucket(members, new Transformer() {
+ public Object transform(Object value) {
+ return new Integer(typer.getParameterTypes((MethodInfo)value).length);
+ }
+ });
+ e.dup();
+ e.arraylength();
+ e.process_switch(EmitUtils.getSwitchKeys(buckets), new ProcessSwitchCallback() {
+ public void processCase(int key, Label dontUseEnd) throws Exception {
+ List bucket = (List)buckets.get(new Integer(key));
+ member_helper_type(e, bucket, callback, typer, def, end, new BitSet());
+ }
+ public void processDefault() throws Exception {
+ e.goTo(def);
+ }
+ });
+ }
+
+ private static void member_helper_type(final CodeEmitter e,
+ List members,
+ final ObjectSwitchCallback callback,
+ final ParameterTyper typer,
+ final Label def,
+ final Label end,
+ final BitSet checked) throws Exception {
+ if (members.size() == 1) {
+ MethodInfo member = (MethodInfo)members.get(0);
+ Type[] types = typer.getParameterTypes(member);
+ // need to check classes that have not already been checked via switches
+ for (int i = 0; i < types.length; i++) {
+ if (checked == null || !checked.get(i)) {
+ e.dup();
+ e.aaload(i);
+ e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
+ e.push(TypeUtils.emulateClassGetName(types[i]));
+ e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
+ e.if_jump(e.EQ, def);
+ }
+ }
+ e.pop();
+ callback.processCase(member, end);
+ } else {
+ // choose the index that has the best chance of uniquely identifying member
+ Type[] example = typer.getParameterTypes((MethodInfo)members.get(0));
+ Map buckets = null;
+ int index = -1;
+ for (int i = 0; i < example.length; i++) {
+ final int j = i;
+ Map test = CollectionUtils.bucket(members, new Transformer() {
+ public Object transform(Object value) {
+ return TypeUtils.emulateClassGetName(typer.getParameterTypes((MethodInfo)value)[j]);
+ }
+ });
+ if (buckets == null || test.size() > buckets.size()) {
+ buckets = test;
+ index = i;
+ }
+ }
+ if (buckets == null || buckets.size() == 1) {
+ // TODO: switch by returnType
+ // must have two methods with same name, types, and different return types
+ e.goTo(def);
+ } else {
+ checked.set(index);
+
+ e.dup();
+ e.aaload(index);
+ e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
+
+ final Map fbuckets = buckets;
+ String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]);
+ EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label dontUseEnd) throws Exception {
+ member_helper_type(e, (List)fbuckets.get(key), callback, typer, def, end, checked);
+ }
+ public void processDefault() throws Exception {
+ e.goTo(def);
+ }
+ });
+ }
+ }
+ }
+
+ public static void wrap_throwable(Block block, Type wrapper) {
+ CodeEmitter e = block.getCodeEmitter();
+ e.catch_exception(block, Constants.TYPE_THROWABLE);
+ e.new_instance(wrapper);
+ e.dup_x1();
+ e.swap();
+ e.invoke_constructor(wrapper, CSTRUCT_THROWABLE);
+ e.athrow();
+ }
+
+ public static void add_properties(ClassEmitter ce, String[] names, Type[] types) {
+ for (int i = 0; i < names.length; i++) {
+ String fieldName = "$cglib_prop_" + names[i];
+ ce.declare_field(Constants.ACC_PRIVATE, fieldName, types[i], null);
+ EmitUtils.add_property(ce, names[i], types[i], fieldName);
+ }
+ }
+
+ public static void add_property(ClassEmitter ce, String name, Type type, String fieldName) {
+ String property = TypeUtils.upperFirst(name);
+ CodeEmitter e;
+ e = ce.begin_method(Constants.ACC_PUBLIC,
+ new Signature("get" + property,
+ type,
+ Constants.TYPES_EMPTY),
+ null);
+ e.load_this();
+ e.getfield(fieldName);
+ e.return_value();
+ e.end_method();
+
+ e = ce.begin_method(Constants.ACC_PUBLIC,
+ new Signature("set" + property,
+ Type.VOID_TYPE,
+ new Type[]{ type }),
+ null);
+ e.load_this();
+ e.load_arg(0);
+ e.putfield(fieldName);
+ e.return_value();
+ e.end_method();
+ }
+
+ /* generates:
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Error e) {
+ throw e;
+ } catch (<DeclaredException> e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new <Wrapper>(e);
+ }
+ */
+ public static void wrap_undeclared_throwable(CodeEmitter e, Block handler, Type[] exceptions, Type wrapper) {
+ Set set = (exceptions == null) ? Collections.EMPTY_SET : new HashSet(Arrays.asList(exceptions));
+
+ if (set.contains(Constants.TYPE_THROWABLE))
+ return;
+
+ boolean needThrow = exceptions != null;
+ if (!set.contains(Constants.TYPE_RUNTIME_EXCEPTION)) {
+ e.catch_exception(handler, Constants.TYPE_RUNTIME_EXCEPTION);
+ needThrow = true;
+ }
+ if (!set.contains(Constants.TYPE_ERROR)) {
+ e.catch_exception(handler, Constants.TYPE_ERROR);
+ needThrow = true;
+ }
+ if (exceptions != null) {
+ for (int i = 0; i < exceptions.length; i++) {
+ e.catch_exception(handler, exceptions[i]);
+ }
+ }
+ if (needThrow) {
+ e.athrow();
+ }
+ // e -> eo -> oeo -> ooe -> o
+ e.catch_exception(handler, Constants.TYPE_THROWABLE);
+ e.new_instance(wrapper);
+ e.dup_x1();
+ e.swap();
+ e.invoke_constructor(wrapper, CSTRUCT_THROWABLE);
+ e.athrow();
+ }
+
+ public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method) {
+ return begin_method(e, method, method.getModifiers());
+ }
+
+ public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method, int access) {
+ return e.begin_method(access,
+ method.getSignature(),
+ method.getExceptionTypes());
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/GeneratorStrategy.java b/cglib-and-asm/src/org/mockito/cglib/core/GeneratorStrategy.java
new file mode 100644
index 0000000..dee3773
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/GeneratorStrategy.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+/**
+ * The <code>GeneratorStrategy</code. is responsible for taking a
+ * {@link ClassGenerator} and producing a byte array containing the
+ * data for the generated <code>Class</code>. By providing your
+ * own strategy you may examine or modify the generated class before
+ * it is loaded. Typically this will be accomplished by subclassing
+ * {@link DefaultGeneratorStrategy} and overriding the appropriate
+ * protected method.
+ * @see AbstractClassGenerator#setStrategy
+ */
+public interface GeneratorStrategy {
+ /**
+ * Generate the class.
+ * @param cg a class generator on which you can call {@link ClassGenerator#generateClass}
+ * @return a byte array containing the bits of a valid Class
+ */
+ byte[] generate(ClassGenerator cg) throws Exception;
+
+ /**
+ * The <code>GeneratorStrategy</code> in use does not currently, but may
+ * in the future, affect the caching of classes generated by {@link
+ * AbstractClassGenerator}, so this is a reminder that you should
+ * correctly implement <code>equals</code> and <code>hashCode</code>
+ * to avoid generating too many classes.
+ */
+ boolean equals(Object o);
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/KeyFactory.java b/cglib-and-asm/src/org/mockito/cglib/core/KeyFactory.java
new file mode 100644
index 0000000..e0f5ab5
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/KeyFactory.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mockito.cglib.core;
+
+import java.lang.reflect.Method;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Label;
+import org.mockito.asm.Type;
+
+/**
+ * Generates classes to handle multi-valued keys, for use in things such as Maps and Sets.
+ * Code for <code>equals</code> and <code>hashCode</code> methods follow the
+ * the rules laid out in <i>Effective Java</i> by Joshua Bloch.
+ * <p>
+ * To generate a <code>KeyFactory</code>, you need to supply an interface which
+ * describes the structure of the key. The interface should have a
+ * single method named <code>newInstance</code>, which returns an
+ * <code>Object</code>. The arguments array can be
+ * <i>anything</i>--Objects, primitive values, or single or
+ * multi-dimension arrays of either. For example:
+ * <p><pre>
+ * private interface IntStringKey {
+ * public Object newInstance(int i, String s);
+ * }
+ * </pre><p>
+ * Once you have made a <code>KeyFactory</code>, you generate a new key by calling
+ * the <code>newInstance</code> method defined by your interface.
+ * <p><pre>
+ * IntStringKey factory = (IntStringKey)KeyFactory.create(IntStringKey.class);
+ * Object key1 = factory.newInstance(4, "Hello");
+ * Object key2 = factory.newInstance(4, "World");
+ * </pre><p>
+ * <b>Note:</b>
+ * <code>hashCode</code> equality between two keys <code>key1</code> and <code>key2</code> is only guaranteed if
+ * <code>key1.equals(key2)</code> <i>and</i> the keys were produced by the same factory.
+ *
+ * @version $Id: KeyFactory.java,v 1.26 2006/03/05 02:43:19 herbyderby Exp $
+ */
+abstract public class KeyFactory {
+ private static final Signature GET_NAME =
+ TypeUtils.parseSignature("String getName()");
+ private static final Signature GET_CLASS =
+ TypeUtils.parseSignature("Class getClass()");
+ private static final Signature HASH_CODE =
+ TypeUtils.parseSignature("int hashCode()");
+ private static final Signature EQUALS =
+ TypeUtils.parseSignature("boolean equals(Object)");
+ private static final Signature TO_STRING =
+ TypeUtils.parseSignature("String toString()");
+ private static final Signature APPEND_STRING =
+ TypeUtils.parseSignature("StringBuffer append(String)");
+ private static final Type KEY_FACTORY =
+ TypeUtils.parseType("org.mockito.cglib.core.KeyFactory");
+
+ //generated numbers:
+ private final static int PRIMES[] = {
+ 11, 73, 179, 331,
+ 521, 787, 1213, 1823,
+ 2609, 3691, 5189, 7247,
+ 10037, 13931, 19289, 26627,
+ 36683, 50441, 69403, 95401,
+ 131129, 180179, 247501, 340057,
+ 467063, 641371, 880603, 1209107,
+ 1660097, 2279161, 3129011, 4295723,
+ 5897291, 8095873, 11114263, 15257791,
+ 20946017, 28754629, 39474179, 54189869,
+ 74391461, 102123817, 140194277, 192456917,
+ 264202273, 362693231, 497900099, 683510293,
+ 938313161, 1288102441, 1768288259 };
+
+
+ public static final Customizer CLASS_BY_NAME = new Customizer() {
+ public void customize(CodeEmitter e, Type type) {
+ if (type.equals(Constants.TYPE_CLASS)) {
+ e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
+ }
+ }
+ };
+
+ public static final Customizer OBJECT_BY_CLASS = new Customizer() {
+ public void customize(CodeEmitter e, Type type) {
+ e.invoke_virtual(Constants.TYPE_OBJECT, GET_CLASS);
+ }
+ };
+
+ protected KeyFactory() {
+ }
+
+ public static KeyFactory create(Class keyInterface) {
+ return create(keyInterface, null);
+ }
+
+ public static KeyFactory create(Class keyInterface, Customizer customizer) {
+ return create(keyInterface.getClassLoader(), keyInterface, customizer);
+ }
+
+ public static KeyFactory create(ClassLoader loader, Class keyInterface, Customizer customizer) {
+ Generator gen = new Generator();
+ gen.setInterface(keyInterface);
+ gen.setCustomizer(customizer);
+ gen.setClassLoader(loader);
+ return gen.create();
+ }
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(KeyFactory.class.getName());
+ private Class keyInterface;
+ private Customizer customizer;
+ private int constant;
+ private int multiplier;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return keyInterface.getClassLoader();
+ }
+
+ public void setCustomizer(Customizer customizer) {
+ this.customizer = customizer;
+ }
+
+ public void setInterface(Class keyInterface) {
+ this.keyInterface = keyInterface;
+ }
+
+ public KeyFactory create() {
+ setNamePrefix(keyInterface.getName());
+ return (KeyFactory)super.create(keyInterface.getName());
+ }
+
+ public void setHashConstant(int constant) {
+ this.constant = constant;
+ }
+
+ public void setHashMultiplier(int multiplier) {
+ this.multiplier = multiplier;
+ }
+
+ protected Object firstInstance(Class type) {
+ return ReflectUtils.newInstance(type);
+ }
+
+ protected Object nextInstance(Object instance) {
+ return instance;
+ }
+
+ public void generateClass(ClassVisitor v) {
+ ClassEmitter ce = new ClassEmitter(v);
+
+ Method newInstance = ReflectUtils.findNewInstance(keyInterface);
+ if (!newInstance.getReturnType().equals(Object.class)) {
+ throw new IllegalArgumentException("newInstance method must return Object");
+ }
+
+ Type[] parameterTypes = TypeUtils.getTypes(newInstance.getParameterTypes());
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ KEY_FACTORY,
+ new Type[]{ Type.getType(keyInterface) },
+ Constants.SOURCE_FILE);
+ EmitUtils.null_constructor(ce);
+ EmitUtils.factory_method(ce, ReflectUtils.getSignature(newInstance));
+
+ int seed = 0;
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
+ TypeUtils.parseConstructor(parameterTypes),
+ null);
+ e.load_this();
+ e.super_invoke_constructor();
+ e.load_this();
+ for (int i = 0; i < parameterTypes.length; i++) {
+ seed += parameterTypes[i].hashCode();
+ ce.declare_field(Constants.ACC_PRIVATE | Constants.ACC_FINAL,
+ getFieldName(i),
+ parameterTypes[i],
+ null);
+ e.dup();
+ e.load_arg(i);
+ e.putfield(getFieldName(i));
+ }
+ e.return_value();
+ e.end_method();
+
+ // hash code
+ e = ce.begin_method(Constants.ACC_PUBLIC, HASH_CODE, null);
+ int hc = (constant != 0) ? constant : PRIMES[(int)(Math.abs(seed) % PRIMES.length)];
+ int hm = (multiplier != 0) ? multiplier : PRIMES[(int)(Math.abs(seed * 13) % PRIMES.length)];
+ e.push(hc);
+ for (int i = 0; i < parameterTypes.length; i++) {
+ e.load_this();
+ e.getfield(getFieldName(i));
+ EmitUtils.hash_code(e, parameterTypes[i], hm, customizer);
+ }
+ e.return_value();
+ e.end_method();
+
+ // equals
+ e = ce.begin_method(Constants.ACC_PUBLIC, EQUALS, null);
+ Label fail = e.make_label();
+ e.load_arg(0);
+ e.instance_of_this();
+ e.if_jump(e.EQ, fail);
+ for (int i = 0; i < parameterTypes.length; i++) {
+ e.load_this();
+ e.getfield(getFieldName(i));
+ e.load_arg(0);
+ e.checkcast_this();
+ e.getfield(getFieldName(i));
+ EmitUtils.not_equals(e, parameterTypes[i], fail, customizer);
+ }
+ e.push(1);
+ e.return_value();
+ e.mark(fail);
+ e.push(0);
+ e.return_value();
+ e.end_method();
+
+ // toString
+ e = ce.begin_method(Constants.ACC_PUBLIC, TO_STRING, null);
+ e.new_instance(Constants.TYPE_STRING_BUFFER);
+ e.dup();
+ e.invoke_constructor(Constants.TYPE_STRING_BUFFER);
+ for (int i = 0; i < parameterTypes.length; i++) {
+ if (i > 0) {
+ e.push(", ");
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ }
+ e.load_this();
+ e.getfield(getFieldName(i));
+ EmitUtils.append_string(e, parameterTypes[i], EmitUtils.DEFAULT_DELIMITERS, customizer);
+ }
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING);
+ e.return_value();
+ e.end_method();
+
+ ce.end_class();
+ }
+
+ private String getFieldName(int arg) {
+ return "FIELD_" + arg;
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/Local.java b/cglib-and-asm/src/org/mockito/cglib/core/Local.java
new file mode 100644
index 0000000..55e815b
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/Local.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.Type;
+
+public class Local
+{
+ private Type type;
+ private int index;
+
+ public Local(int index, Type type) {
+ this.type = type;
+ this.index = index;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public Type getType() {
+ return type;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/LocalVariablesSorter.java b/cglib-and-asm/src/org/mockito/cglib/core/LocalVariablesSorter.java
new file mode 100644
index 0000000..1daff06
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/LocalVariablesSorter.java
@@ -0,0 +1,159 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.Label;
+import org.mockito.asm.MethodAdapter;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Opcodes;
+import org.mockito.asm.Type;
+
+/**
+ * A {@link MethodAdapter} that renumbers local variables in their order of
+ * appearance. This adapter allows one to easily add new local variables to a
+ * method.
+ *
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public class LocalVariablesSorter extends MethodAdapter {
+
+ /**
+ * Mapping from old to new local variable indexes. A local variable at index
+ * i of size 1 is remapped to 'mapping[2*i]', while a local variable at
+ * index i of size 2 is remapped to 'mapping[2*i+1]'.
+ */
+ private static class State
+ {
+ int[] mapping = new int[40];
+ int nextLocal;
+ }
+
+ protected final int firstLocal;
+ private final State state;
+
+ public LocalVariablesSorter(
+ final int access,
+ final String desc,
+ final MethodVisitor mv)
+ {
+ super(mv);
+ state = new State();
+ Type[] args = Type.getArgumentTypes(desc);
+ state.nextLocal = ((Opcodes.ACC_STATIC & access) != 0) ? 0 : 1;
+ for (int i = 0; i < args.length; i++) {
+ state.nextLocal += args[i].getSize();
+ }
+ firstLocal = state.nextLocal;
+ }
+
+ public LocalVariablesSorter(LocalVariablesSorter lvs) {
+ super(lvs.mv);
+ state = lvs.state;
+ firstLocal = lvs.firstLocal;
+ }
+
+ public void visitVarInsn(final int opcode, final int var) {
+ int size;
+ switch (opcode) {
+ case Opcodes.LLOAD:
+ case Opcodes.LSTORE:
+ case Opcodes.DLOAD:
+ case Opcodes.DSTORE:
+ size = 2;
+ break;
+ default:
+ size = 1;
+ }
+ mv.visitVarInsn(opcode, remap(var, size));
+ }
+
+ public void visitIincInsn(final int var, final int increment) {
+ mv.visitIincInsn(remap(var, 1), increment);
+ }
+
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ mv.visitMaxs(maxStack, state.nextLocal);
+ }
+
+ public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index)
+ {
+ mv.visitLocalVariable(name, desc, signature, start, end, remap(index));
+ }
+
+ // -------------
+
+ protected int newLocal(final int size) {
+ int var = state.nextLocal;
+ state.nextLocal += size;
+ return var;
+ }
+
+ private int remap(final int var, final int size) {
+ if (var < firstLocal) {
+ return var;
+ }
+ int key = 2 * var + size - 1;
+ int length = state.mapping.length;
+ if (key >= length) {
+ int[] newMapping = new int[Math.max(2 * length, key + 1)];
+ System.arraycopy(state.mapping, 0, newMapping, 0, length);
+ state.mapping = newMapping;
+ }
+ int value = state.mapping[key];
+ if (value == 0) {
+ value = state.nextLocal + 1;
+ state.mapping[key] = value;
+ state.nextLocal += size;
+ }
+ return value - 1;
+ }
+
+ private int remap(final int var) {
+ if (var < firstLocal) {
+ return var;
+ }
+ int key = 2 * var;
+ int value = key < state.mapping.length ? state.mapping[key] : 0;
+ if (value == 0) {
+ value = key + 1 < state.mapping.length ? state.mapping[key + 1] : 0;
+ }
+ if (value == 0) {
+ throw new IllegalStateException("Unknown local variable " + var);
+ }
+ return value - 1;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/MethodInfo.java b/cglib-and-asm/src/org/mockito/cglib/core/MethodInfo.java
new file mode 100644
index 0000000..4fb32b4
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/MethodInfo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.Type;
+
+abstract public class MethodInfo {
+
+ protected MethodInfo() {
+ }
+
+ abstract public ClassInfo getClassInfo();
+ abstract public int getModifiers();
+ abstract public Signature getSignature();
+ abstract public Type[] getExceptionTypes();
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (!(o instanceof MethodInfo))
+ return false;
+ return getSignature().equals(((MethodInfo)o).getSignature());
+ }
+
+ public int hashCode() {
+ return getSignature().hashCode();
+ }
+
+ public String toString() {
+ // TODO: include modifiers, exceptions
+ return getSignature().toString();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/MethodInfoTransformer.java b/cglib-and-asm/src/org/mockito/cglib/core/MethodInfoTransformer.java
new file mode 100644
index 0000000..62bc696
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/MethodInfoTransformer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.lang.reflect.*;
+
+public class MethodInfoTransformer implements Transformer
+{
+ private static final MethodInfoTransformer INSTANCE = new MethodInfoTransformer();
+
+ public static MethodInfoTransformer getInstance() {
+ return INSTANCE;
+ }
+
+ public Object transform(Object value) {
+ if (value instanceof Method) {
+ return ReflectUtils.getMethodInfo((Method)value);
+ } else if (value instanceof Constructor) {
+ return ReflectUtils.getMethodInfo((Constructor)value);
+ } else {
+ throw new IllegalArgumentException("cannot get method info for " + value);
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/MethodWrapper.java b/cglib-and-asm/src/org/mockito/cglib/core/MethodWrapper.java
new file mode 100644
index 0000000..34a6984
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/MethodWrapper.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+public class MethodWrapper {
+ private static final MethodWrapperKey KEY_FACTORY =
+ (MethodWrapperKey)KeyFactory.create(MethodWrapperKey.class);
+
+ /** Internal interface, only public due to ClassLoader issues. */
+ public interface MethodWrapperKey {
+ public Object newInstance(String name, String[] parameterTypes, String returnType);
+ }
+
+ private MethodWrapper() {
+ }
+
+ public static Object create(Method method) {
+ return KEY_FACTORY.newInstance(method.getName(),
+ ReflectUtils.getNames(method.getParameterTypes()),
+ method.getReturnType().getName());
+ }
+
+ public static Set createSet(Collection methods) {
+ Set set = new HashSet();
+ for (Iterator it = methods.iterator(); it.hasNext();) {
+ set.add(create((Method)it.next()));
+ }
+ return set;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/NamingPolicy.java b/cglib-and-asm/src/org/mockito/cglib/core/NamingPolicy.java
new file mode 100644
index 0000000..cf0b408
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/NamingPolicy.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.util.Set;
+
+/**
+ * Customize the generated class name for {@link AbstractClassGenerator}-based utilities.
+ */
+public interface NamingPolicy {
+ /**
+ * Choose a name for a generated class.
+ * @param prefix a dotted-name chosen by the generating class (possibly to put the generated class in a particular package)
+ * @param source the fully-qualified class name of the generating class (for example "org.mockito.cglib.Enhancer")
+ * @param key A key object representing the state of the parameters; for caching to work properly, equal keys should result
+ * in the same generated class name. The default policy incorporates <code>key.hashCode()</code> into the class name.
+ * @param names a predicate that returns true if the given classname has already been used in the same ClassLoader.
+ * @return the fully-qualified class name
+ */
+ String getClassName(String prefix, String source, Object key, Predicate names);
+
+ /**
+ * The <code>NamingPolicy</code> in use does not currently, but may
+ * in the future, affect the caching of classes generated by {@link
+ * AbstractClassGenerator}, so this is a reminder that you should
+ * correctly implement <code>equals</code> and <code>hashCode</code>
+ * to avoid generating too many classes.
+ */
+ boolean equals(Object o);
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/ObjectSwitchCallback.java b/cglib-and-asm/src/org/mockito/cglib/core/ObjectSwitchCallback.java
new file mode 100644
index 0000000..ed52fd8
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/ObjectSwitchCallback.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.Label;
+
+public interface ObjectSwitchCallback {
+ void processCase(Object key, Label end) throws Exception;
+ void processDefault() throws Exception;
+}
+
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/Predicate.java b/cglib-and-asm/src/org/mockito/cglib/core/Predicate.java
new file mode 100644
index 0000000..b014dc8
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/Predicate.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+public interface Predicate {
+ boolean evaluate(Object arg);
+}
+
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/ProcessArrayCallback.java b/cglib-and-asm/src/org/mockito/cglib/core/ProcessArrayCallback.java
new file mode 100644
index 0000000..fd85bce
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/ProcessArrayCallback.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.Type;
+
+public interface ProcessArrayCallback {
+ void processElement(Type type);
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/ProcessSwitchCallback.java b/cglib-and-asm/src/org/mockito/cglib/core/ProcessSwitchCallback.java
new file mode 100644
index 0000000..8fbe4d5
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/ProcessSwitchCallback.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.Label;
+
+public interface ProcessSwitchCallback {
+ void processCase(int key, Label end) throws Exception;
+ void processDefault() throws Exception;
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/ReflectUtils.java b/cglib-and-asm/src/org/mockito/cglib/core/ReflectUtils.java
new file mode 100644
index 0000000..bbc388b
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/ReflectUtils.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.beans.*;
+import java.lang.reflect.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.*;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.Type;
+
+/**
+ * @version $Id: ReflectUtils.java,v 1.29 2006/02/28 00:30:51 herbyderby Exp $
+ */
+public class ReflectUtils {
+ private ReflectUtils() { }
+
+ private static final Map primitives = new HashMap(8);
+ private static final Map transforms = new HashMap(8);
+ private static final ClassLoader defaultLoader = ReflectUtils.class.getClassLoader();
+ private static Method DEFINE_CLASS;
+ private static final ProtectionDomain PROTECTION_DOMAIN;
+
+ static {
+ PROTECTION_DOMAIN = (ProtectionDomain)AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return ReflectUtils.class.getProtectionDomain();
+ }
+ });
+
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ try {
+ Class loader = Class.forName("java.lang.ClassLoader"); // JVM crash w/o this
+ DEFINE_CLASS = loader.getDeclaredMethod("defineClass",
+ new Class[]{ String.class,
+ byte[].class,
+ Integer.TYPE,
+ Integer.TYPE,
+ ProtectionDomain.class });
+ DEFINE_CLASS.setAccessible(true);
+ } catch (ClassNotFoundException e) {
+ throw new CodeGenerationException(e);
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ return null;
+ }
+ });
+ }
+
+ private static final String[] CGLIB_PACKAGES = {
+ "java.lang",
+ };
+
+ static {
+ primitives.put("byte", Byte.TYPE);
+ primitives.put("char", Character.TYPE);
+ primitives.put("double", Double.TYPE);
+ primitives.put("float", Float.TYPE);
+ primitives.put("int", Integer.TYPE);
+ primitives.put("long", Long.TYPE);
+ primitives.put("short", Short.TYPE);
+ primitives.put("boolean", Boolean.TYPE);
+
+ transforms.put("byte", "B");
+ transforms.put("char", "C");
+ transforms.put("double", "D");
+ transforms.put("float", "F");
+ transforms.put("int", "I");
+ transforms.put("long", "J");
+ transforms.put("short", "S");
+ transforms.put("boolean", "Z");
+ }
+
+ public static Type[] getExceptionTypes(Member member) {
+ if (member instanceof Method) {
+ return TypeUtils.getTypes(((Method)member).getExceptionTypes());
+ } else if (member instanceof Constructor) {
+ return TypeUtils.getTypes(((Constructor)member).getExceptionTypes());
+ } else {
+ throw new IllegalArgumentException("Cannot get exception types of a field");
+ }
+ }
+
+ public static Signature getSignature(Member member) {
+ if (member instanceof Method) {
+ return new Signature(member.getName(), Type.getMethodDescriptor((Method)member));
+ } else if (member instanceof Constructor) {
+ Type[] types = TypeUtils.getTypes(((Constructor)member).getParameterTypes());
+ return new Signature(Constants.CONSTRUCTOR_NAME,
+ Type.getMethodDescriptor(Type.VOID_TYPE, types));
+
+ } else {
+ throw new IllegalArgumentException("Cannot get signature of a field");
+ }
+ }
+
+ public static Constructor findConstructor(String desc) {
+ return findConstructor(desc, defaultLoader);
+ }
+
+ public static Constructor findConstructor(String desc, ClassLoader loader) {
+ try {
+ int lparen = desc.indexOf('(');
+ String className = desc.substring(0, lparen).trim();
+ return getClass(className, loader).getConstructor(parseTypes(desc, loader));
+ } catch (ClassNotFoundException e) {
+ throw new CodeGenerationException(e);
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ public static Method findMethod(String desc) {
+ return findMethod(desc, defaultLoader);
+ }
+
+ public static Method findMethod(String desc, ClassLoader loader) {
+ try {
+ int lparen = desc.indexOf('(');
+ int dot = desc.lastIndexOf('.', lparen);
+ String className = desc.substring(0, dot).trim();
+ String methodName = desc.substring(dot + 1, lparen).trim();
+ return getClass(className, loader).getDeclaredMethod(methodName, parseTypes(desc, loader));
+ } catch (ClassNotFoundException e) {
+ throw new CodeGenerationException(e);
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ private static Class[] parseTypes(String desc, ClassLoader loader) throws ClassNotFoundException {
+ int lparen = desc.indexOf('(');
+ int rparen = desc.indexOf(')', lparen);
+ List params = new ArrayList();
+ int start = lparen + 1;
+ for (;;) {
+ int comma = desc.indexOf(',', start);
+ if (comma < 0) {
+ break;
+ }
+ params.add(desc.substring(start, comma).trim());
+ start = comma + 1;
+ }
+ if (start < rparen) {
+ params.add(desc.substring(start, rparen).trim());
+ }
+ Class[] types = new Class[params.size()];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = getClass((String)params.get(i), loader);
+ }
+ return types;
+ }
+
+ private static Class getClass(String className, ClassLoader loader) throws ClassNotFoundException {
+ return getClass(className, loader, CGLIB_PACKAGES);
+ }
+
+ private static Class getClass(String className, ClassLoader loader, String[] packages) throws ClassNotFoundException {
+ String save = className;
+ int dimensions = 0;
+ int index = 0;
+ while ((index = className.indexOf("[]", index) + 1) > 0) {
+ dimensions++;
+ }
+ StringBuffer brackets = new StringBuffer(className.length() - dimensions);
+ for (int i = 0; i < dimensions; i++) {
+ brackets.append('[');
+ }
+ className = className.substring(0, className.length() - 2 * dimensions);
+
+ String prefix = (dimensions > 0) ? brackets + "L" : "";
+ String suffix = (dimensions > 0) ? ";" : "";
+ try {
+ return Class.forName(prefix + className + suffix, false, loader);
+ } catch (ClassNotFoundException ignore) { }
+ for (int i = 0; i < packages.length; i++) {
+ try {
+ return Class.forName(prefix + packages[i] + '.' + className + suffix, false, loader);
+ } catch (ClassNotFoundException ignore) { }
+ }
+ if (dimensions == 0) {
+ Class c = (Class)primitives.get(className);
+ if (c != null) {
+ return c;
+ }
+ } else {
+ String transform = (String)transforms.get(className);
+ if (transform != null) {
+ try {
+ return Class.forName(brackets + transform, false, loader);
+ } catch (ClassNotFoundException ignore) { }
+ }
+ }
+ throw new ClassNotFoundException(save);
+ }
+
+
+ public static Object newInstance(Class type) {
+ return newInstance(type, Constants.EMPTY_CLASS_ARRAY, null);
+ }
+
+ public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) {
+ return newInstance(getConstructor(type, parameterTypes), args);
+ }
+
+ public static Object newInstance(final Constructor cstruct, final Object[] args) {
+
+ boolean flag = cstruct.isAccessible();
+ try {
+ cstruct.setAccessible(true);
+ Object result = cstruct.newInstance(args);
+ return result;
+ } catch (InstantiationException e) {
+ throw new CodeGenerationException(e);
+ } catch (IllegalAccessException e) {
+ throw new CodeGenerationException(e);
+ } catch (InvocationTargetException e) {
+ throw new CodeGenerationException(e.getTargetException());
+ } finally {
+ cstruct.setAccessible(flag);
+ }
+
+ }
+
+ public static Constructor getConstructor(Class type, Class[] parameterTypes) {
+ try {
+ Constructor constructor = type.getDeclaredConstructor(parameterTypes);
+ constructor.setAccessible(true);
+ return constructor;
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ public static String[] getNames(Class[] classes)
+ {
+ if (classes == null)
+ return null;
+ String[] names = new String[classes.length];
+ for (int i = 0; i < names.length; i++) {
+ names[i] = classes[i].getName();
+ }
+ return names;
+ }
+
+ public static Class[] getClasses(Object[] objects) {
+ Class[] classes = new Class[objects.length];
+ for (int i = 0; i < objects.length; i++) {
+ classes[i] = objects[i].getClass();
+ }
+ return classes;
+ }
+
+ public static Method findNewInstance(Class iface) {
+ Method m = findInterfaceMethod(iface);
+ if (!m.getName().equals("newInstance")) {
+ throw new IllegalArgumentException(iface + " missing newInstance method");
+ }
+ return m;
+ }
+
+ public static Method[] getPropertyMethods(PropertyDescriptor[] properties, boolean read, boolean write) {
+ Set methods = new HashSet();
+ for (int i = 0; i < properties.length; i++) {
+ PropertyDescriptor pd = properties[i];
+ if (read) {
+ methods.add(pd.getReadMethod());
+ }
+ if (write) {
+ methods.add(pd.getWriteMethod());
+ }
+ }
+ methods.remove(null);
+ return (Method[])methods.toArray(new Method[methods.size()]);
+ }
+
+ public static PropertyDescriptor[] getBeanProperties(Class type) {
+ return getPropertiesHelper(type, true, true);
+ }
+
+ public static PropertyDescriptor[] getBeanGetters(Class type) {
+ return getPropertiesHelper(type, true, false);
+ }
+
+ public static PropertyDescriptor[] getBeanSetters(Class type) {
+ return getPropertiesHelper(type, false, true);
+ }
+
+ private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) {
+ try {
+ BeanInfo info = Introspector.getBeanInfo(type, Object.class);
+ PropertyDescriptor[] all = info.getPropertyDescriptors();
+ if (read && write) {
+ return all;
+ }
+ List properties = new ArrayList(all.length);
+ for (int i = 0; i < all.length; i++) {
+ PropertyDescriptor pd = all[i];
+ if ((read && pd.getReadMethod() != null) ||
+ (write && pd.getWriteMethod() != null)) {
+ properties.add(pd);
+ }
+ }
+ return (PropertyDescriptor[])properties.toArray(new PropertyDescriptor[properties.size()]);
+ } catch (IntrospectionException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+
+
+ public static Method findDeclaredMethod(final Class type,
+ final String methodName, final Class[] parameterTypes)
+ throws NoSuchMethodException {
+
+ Class cl = type;
+ while (cl != null) {
+ try {
+ return cl.getDeclaredMethod(methodName, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ cl = cl.getSuperclass();
+ }
+ }
+ throw new NoSuchMethodException(methodName);
+
+ }
+
+ public static List addAllMethods(final Class type, final List list) {
+
+
+ list.addAll(java.util.Arrays.asList(type.getDeclaredMethods()));
+ Class superclass = type.getSuperclass();
+ if (superclass != null) {
+ addAllMethods(superclass, list);
+ }
+ Class[] interfaces = type.getInterfaces();
+ for (int i = 0; i < interfaces.length; i++) {
+ addAllMethods(interfaces[i], list);
+ }
+
+ return list;
+ }
+
+ public static List addAllInterfaces(Class type, List list) {
+ Class superclass = type.getSuperclass();
+ if (superclass != null) {
+ list.addAll(Arrays.asList(type.getInterfaces()));
+ addAllInterfaces(superclass, list);
+ }
+ return list;
+ }
+
+
+ public static Method findInterfaceMethod(Class iface) {
+ if (!iface.isInterface()) {
+ throw new IllegalArgumentException(iface + " is not an interface");
+ }
+ Method[] methods = iface.getDeclaredMethods();
+ if (methods.length != 1) {
+ throw new IllegalArgumentException("expecting exactly 1 method in " + iface);
+ }
+ return methods[0];
+ }
+
+ public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception {
+ Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), PROTECTION_DOMAIN };
+ return (Class)DEFINE_CLASS.invoke(loader, args);
+ }
+
+ public static int findPackageProtected(Class[] classes) {
+ for (int i = 0; i < classes.length; i++) {
+ if (!Modifier.isPublic(classes[i].getModifiers())) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ public static MethodInfo getMethodInfo(final Member member, final int modifiers) {
+ final Signature sig = getSignature(member);
+ return new MethodInfo() {
+ private ClassInfo ci;
+ public ClassInfo getClassInfo() {
+ if (ci == null)
+ ci = ReflectUtils.getClassInfo(member.getDeclaringClass());
+ return ci;
+ }
+ public int getModifiers() {
+ return modifiers;
+ }
+ public Signature getSignature() {
+ return sig;
+ }
+ public Type[] getExceptionTypes() {
+ return ReflectUtils.getExceptionTypes(member);
+ }
+ public Attribute getAttribute() {
+ return null;
+ }
+ };
+ }
+
+ public static MethodInfo getMethodInfo(Member member) {
+ return getMethodInfo(member, member.getModifiers());
+ }
+
+ public static ClassInfo getClassInfo(final Class clazz) {
+ final Type type = Type.getType(clazz);
+ final Type sc = (clazz.getSuperclass() == null) ? null : Type.getType(clazz.getSuperclass());
+ return new ClassInfo() {
+ public Type getType() {
+ return type;
+ }
+ public Type getSuperType() {
+ return sc;
+ }
+ public Type[] getInterfaces() {
+ return TypeUtils.getTypes(clazz.getInterfaces());
+ }
+ public int getModifiers() {
+ return clazz.getModifiers();
+ }
+ };
+ }
+
+ // used by MethodInterceptorGenerated generated code
+ public static Method[] findMethods(String[] namesAndDescriptors, Method[] methods)
+ {
+ Map map = new HashMap();
+ for (int i = 0; i < methods.length; i++) {
+ Method method = methods[i];
+ map.put(method.getName() + Type.getMethodDescriptor(method), method);
+ }
+ Method[] result = new Method[namesAndDescriptors.length / 2];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = (Method)map.get(namesAndDescriptors[i * 2] + namesAndDescriptors[i * 2 + 1]);
+ if (result[i] == null) {
+ // TODO: error?
+ }
+ }
+ return result;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/RejectModifierPredicate.java b/cglib-and-asm/src/org/mockito/cglib/core/RejectModifierPredicate.java
new file mode 100644
index 0000000..b7255ca
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/RejectModifierPredicate.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.lang.reflect.*;
+
+public class RejectModifierPredicate implements Predicate {
+ private int rejectMask;
+
+ public RejectModifierPredicate(int rejectMask) {
+ this.rejectMask = rejectMask;
+ }
+
+ public boolean evaluate(Object arg) {
+ return (((Member)arg).getModifiers() & rejectMask) == 0;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/Signature.java b/cglib-and-asm/src/org/mockito/cglib/core/Signature.java
new file mode 100644
index 0000000..ebb47b9
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/Signature.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import org.mockito.asm.Type;
+
+/**
+ * A representation of a method signature, containing the method name,
+ * return type, and parameter types.
+ */
+public class Signature {
+ private String name;
+ private String desc;
+
+ public Signature(String name, String desc) {
+ // TODO: better error checking
+ if (name.indexOf('(') >= 0) {
+ throw new IllegalArgumentException("Name '" + name + "' is invalid");
+ }
+ this.name = name;
+ this.desc = desc;
+ }
+
+ public Signature(String name, Type returnType, Type[] argumentTypes) {
+ this(name, Type.getMethodDescriptor(returnType, argumentTypes));
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescriptor() {
+ return desc;
+ }
+
+ public Type getReturnType() {
+ return Type.getReturnType(desc);
+ }
+
+ public Type[] getArgumentTypes() {
+ return Type.getArgumentTypes(desc);
+ }
+
+ public String toString() {
+ return name + desc;
+ }
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (!(o instanceof Signature))
+ return false;
+ Signature other = (Signature)o;
+ return name.equals(other.name) && desc.equals(other.desc);
+ }
+
+ public int hashCode() {
+ return name.hashCode() ^ desc.hashCode();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/TinyBitSet.java b/cglib-and-asm/src/org/mockito/cglib/core/TinyBitSet.java
new file mode 100644
index 0000000..964bcd0
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/TinyBitSet.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+public class TinyBitSet {
+ private static int[] T = new int[256];
+ private int value = 0;
+
+ private static int gcount(int x) {
+ int c = 0;
+ while (x != 0) {
+ c++;
+ x &= (x - 1);
+ }
+ return c;
+ }
+
+ static {
+ for(int j = 0; j < 256; j++) {
+ T[j] = gcount(j);
+ }
+ }
+
+ private static int topbit(int i) {
+ int j;
+ for (j = 0; i != 0; i ^= j) {
+ j = i & -i;
+ }
+ return j;
+ }
+
+ private static int log2(int i) {
+ int j = 0;
+ for (j = 0; i != 0; i >>= 1) {
+ j++;
+ }
+ return j;
+ }
+
+ public int length() {
+ return log2(topbit(value));
+ }
+
+ public int cardinality() {
+ int w = value;
+ int c = 0;
+ while (w != 0) {
+ c += T[w & 255];
+ w >>= 8;
+ }
+ return c;
+ }
+
+ public boolean get(int index) {
+ return (value & (1 << index)) != 0;
+ }
+
+ public void set(int index) {
+ value |= (1 << index);
+ }
+
+ public void clear(int index) {
+ value &= ~(1 << index);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/Transformer.java b/cglib-and-asm/src/org/mockito/cglib/core/Transformer.java
new file mode 100644
index 0000000..dfdc030
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/Transformer.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+public interface Transformer {
+ Object transform(Object value);
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/TypeUtils.java b/cglib-and-asm/src/org/mockito/cglib/core/TypeUtils.java
new file mode 100644
index 0000000..ded95b8
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/TypeUtils.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.util.*;
+
+import org.mockito.asm.Type;
+
+public class TypeUtils {
+ private static final Map transforms = new HashMap();
+ private static final Map rtransforms = new HashMap();
+
+ private TypeUtils() {
+ }
+
+ static {
+ transforms.put("void", "V");
+ transforms.put("byte", "B");
+ transforms.put("char", "C");
+ transforms.put("double", "D");
+ transforms.put("float", "F");
+ transforms.put("int", "I");
+ transforms.put("long", "J");
+ transforms.put("short", "S");
+ transforms.put("boolean", "Z");
+
+ CollectionUtils.reverse(transforms, rtransforms);
+ }
+
+ public static Type getType(String className) {
+ return Type.getType("L" + className.replace('.', '/') + ";");
+ }
+
+ public static boolean isFinal(int access) {
+ return (Constants.ACC_FINAL & access) != 0;
+ }
+
+ public static boolean isStatic(int access) {
+ return (Constants.ACC_STATIC & access) != 0;
+ }
+
+ public static boolean isProtected(int access) {
+ return (Constants.ACC_PROTECTED & access) != 0;
+ }
+
+ public static boolean isPublic(int access) {
+ return (Constants.ACC_PUBLIC & access) != 0;
+ }
+
+ public static boolean isAbstract(int access) {
+ return (Constants.ACC_ABSTRACT & access) != 0;
+ }
+
+ public static boolean isInterface(int access) {
+ return (Constants.ACC_INTERFACE & access) != 0;
+ }
+
+ public static boolean isPrivate(int access) {
+ return (Constants.ACC_PRIVATE & access) != 0;
+ }
+
+ public static boolean isSynthetic(int access) {
+ return (Constants.ACC_SYNTHETIC & access) != 0;
+ }
+
+ // getPackage returns null on JDK 1.2
+ public static String getPackageName(Type type) {
+ return getPackageName(getClassName(type));
+ }
+
+ public static String getPackageName(String className) {
+ int idx = className.lastIndexOf('.');
+ return (idx < 0) ? "" : className.substring(0, idx);
+ }
+
+ public static String upperFirst(String s) {
+ if (s == null || s.length() == 0) {
+ return s;
+ }
+ return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+ }
+
+ public static String getClassName(Type type) {
+ if (isPrimitive(type)) {
+ return (String)rtransforms.get(type.getDescriptor());
+ } else if (isArray(type)) {
+ return getClassName(getComponentType(type)) + "[]";
+ } else {
+ return type.getClassName();
+ }
+ }
+
+ public static Type[] add(Type[] types, Type extra) {
+ if (types == null) {
+ return new Type[]{ extra };
+ } else {
+ List list = Arrays.asList(types);
+ if (list.contains(extra)) {
+ return types;
+ }
+ Type[] copy = new Type[types.length + 1];
+ System.arraycopy(types, 0, copy, 0, types.length);
+ copy[types.length] = extra;
+ return copy;
+ }
+ }
+
+ public static Type[] add(Type[] t1, Type[] t2) {
+ // TODO: set semantics?
+ Type[] all = new Type[t1.length + t2.length];
+ System.arraycopy(t1, 0, all, 0, t1.length);
+ System.arraycopy(t2, 0, all, t1.length, t2.length);
+ return all;
+ }
+
+ public static Type fromInternalName(String name) {
+ // TODO; primitives?
+ return Type.getType("L" + name + ";");
+ }
+
+ public static Type[] fromInternalNames(String[] names) {
+ if (names == null) {
+ return null;
+ }
+ Type[] types = new Type[names.length];
+ for (int i = 0; i < names.length; i++) {
+ types[i] = fromInternalName(names[i]);
+ }
+ return types;
+ }
+
+ public static int getStackSize(Type[] types) {
+ int size = 0;
+ for (int i = 0; i < types.length; i++) {
+ size += types[i].getSize();
+ }
+ return size;
+ }
+
+ public static String[] toInternalNames(Type[] types) {
+ if (types == null) {
+ return null;
+ }
+ String[] names = new String[types.length];
+ for (int i = 0; i < types.length; i++) {
+ names[i] = types[i].getInternalName();
+ }
+ return names;
+ }
+
+ public static Signature parseSignature(String s) {
+ int space = s.indexOf(' ');
+ int lparen = s.indexOf('(', space);
+ int rparen = s.indexOf(')', lparen);
+ String returnType = s.substring(0, space);
+ String methodName = s.substring(space + 1, lparen);
+ StringBuffer sb = new StringBuffer();
+ sb.append('(');
+ for (Iterator it = parseTypes(s, lparen + 1, rparen).iterator(); it.hasNext();) {
+ sb.append(it.next());
+ }
+ sb.append(')');
+ sb.append(map(returnType));
+ return new Signature(methodName, sb.toString());
+ }
+
+ public static Type parseType(String s) {
+ return Type.getType(map(s));
+ }
+
+ public static Type[] parseTypes(String s) {
+ List names = parseTypes(s, 0, s.length());
+ Type[] types = new Type[names.size()];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = Type.getType((String)names.get(i));
+ }
+ return types;
+ }
+
+ public static Signature parseConstructor(Type[] types) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("(");
+ for (int i = 0; i < types.length; i++) {
+ sb.append(types[i].getDescriptor());
+ }
+ sb.append(")");
+ sb.append("V");
+ return new Signature(Constants.CONSTRUCTOR_NAME, sb.toString());
+ }
+
+ public static Signature parseConstructor(String sig) {
+ return parseSignature("void <init>(" + sig + ")"); // TODO
+ }
+
+ private static List parseTypes(String s, int mark, int end) {
+ List types = new ArrayList(5);
+ for (;;) {
+ int next = s.indexOf(',', mark);
+ if (next < 0) {
+ break;
+ }
+ types.add(map(s.substring(mark, next).trim()));
+ mark = next + 1;
+ }
+ types.add(map(s.substring(mark, end).trim()));
+ return types;
+ }
+
+ private static String map(String type) {
+ if (type.equals("")) {
+ return type;
+ }
+ String t = (String)transforms.get(type);
+ if (t != null) {
+ return t;
+ } else if (type.indexOf('.') < 0) {
+ return map("java.lang." + type);
+ } else {
+ StringBuffer sb = new StringBuffer();
+ int index = 0;
+ while ((index = type.indexOf("[]", index) + 1) > 0) {
+ sb.append('[');
+ }
+ type = type.substring(0, type.length() - sb.length() * 2);
+ sb.append('L').append(type.replace('.', '/')).append(';');
+ return sb.toString();
+ }
+ }
+
+ public static Type getBoxedType(Type type) {
+ switch (type.getSort()) {
+ case Type.CHAR:
+ return Constants.TYPE_CHARACTER;
+ case Type.BOOLEAN:
+ return Constants.TYPE_BOOLEAN;
+ case Type.DOUBLE:
+ return Constants.TYPE_DOUBLE;
+ case Type.FLOAT:
+ return Constants.TYPE_FLOAT;
+ case Type.LONG:
+ return Constants.TYPE_LONG;
+ case Type.INT:
+ return Constants.TYPE_INTEGER;
+ case Type.SHORT:
+ return Constants.TYPE_SHORT;
+ case Type.BYTE:
+ return Constants.TYPE_BYTE;
+ default:
+ return type;
+ }
+ }
+
+ public static Type getUnboxedType(Type type) {
+ if (Constants.TYPE_INTEGER.equals(type)) {
+ return Type.INT_TYPE;
+ } else if (Constants.TYPE_BOOLEAN.equals(type)) {
+ return Type.BOOLEAN_TYPE;
+ } else if (Constants.TYPE_DOUBLE.equals(type)) {
+ return Type.DOUBLE_TYPE;
+ } else if (Constants.TYPE_LONG.equals(type)) {
+ return Type.LONG_TYPE;
+ } else if (Constants.TYPE_CHARACTER.equals(type)) {
+ return Type.CHAR_TYPE;
+ } else if (Constants.TYPE_BYTE.equals(type)) {
+ return Type.BYTE_TYPE;
+ } else if (Constants.TYPE_FLOAT.equals(type)) {
+ return Type.FLOAT_TYPE;
+ } else if (Constants.TYPE_SHORT.equals(type)) {
+ return Type.SHORT_TYPE;
+ } else {
+ return type;
+ }
+ }
+
+ public static boolean isArray(Type type) {
+ return type.getSort() == Type.ARRAY;
+ }
+
+ public static Type getComponentType(Type type) {
+ if (!isArray(type)) {
+ throw new IllegalArgumentException("Type " + type + " is not an array");
+ }
+ return Type.getType(type.getDescriptor().substring(1));
+ }
+
+ public static boolean isPrimitive(Type type) {
+ switch (type.getSort()) {
+ case Type.ARRAY:
+ case Type.OBJECT:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ public static String emulateClassGetName(Type type) {
+ if (isArray(type)) {
+ return type.getDescriptor().replace('/', '.');
+ } else {
+ return getClassName(type);
+ }
+ }
+
+ public static boolean isConstructor(MethodInfo method) {
+ return method.getSignature().getName().equals(Constants.CONSTRUCTOR_NAME);
+ }
+
+ public static Type[] getTypes(Class[] classes) {
+ if (classes == null) {
+ return null;
+ }
+ Type[] types = new Type[classes.length];
+ for (int i = 0; i < classes.length; i++) {
+ types[i] = Type.getType(classes[i]);
+ }
+ return types;
+ }
+
+ public static int ICONST(int value) {
+ switch (value) {
+ case -1: return Constants.ICONST_M1;
+ case 0: return Constants.ICONST_0;
+ case 1: return Constants.ICONST_1;
+ case 2: return Constants.ICONST_2;
+ case 3: return Constants.ICONST_3;
+ case 4: return Constants.ICONST_4;
+ case 5: return Constants.ICONST_5;
+ }
+ return -1; // error
+ }
+
+ public static int LCONST(long value) {
+ if (value == 0L) {
+ return Constants.LCONST_0;
+ } else if (value == 1L) {
+ return Constants.LCONST_1;
+ } else {
+ return -1; // error
+ }
+ }
+
+ public static int FCONST(float value) {
+ if (value == 0f) {
+ return Constants.FCONST_0;
+ } else if (value == 1f) {
+ return Constants.FCONST_1;
+ } else if (value == 2f) {
+ return Constants.FCONST_2;
+ } else {
+ return -1; // error
+ }
+ }
+
+ public static int DCONST(double value) {
+ if (value == 0d) {
+ return Constants.DCONST_0;
+ } else if (value == 1d) {
+ return Constants.DCONST_1;
+ } else {
+ return -1; // error
+ }
+ }
+
+ public static int NEWARRAY(Type type) {
+ switch (type.getSort()) {
+ case Type.BYTE:
+ return Constants.T_BYTE;
+ case Type.CHAR:
+ return Constants.T_CHAR;
+ case Type.DOUBLE:
+ return Constants.T_DOUBLE;
+ case Type.FLOAT:
+ return Constants.T_FLOAT;
+ case Type.INT:
+ return Constants.T_INT;
+ case Type.LONG:
+ return Constants.T_LONG;
+ case Type.SHORT:
+ return Constants.T_SHORT;
+ case Type.BOOLEAN:
+ return Constants.T_BOOLEAN;
+ default:
+ return -1; // error
+ }
+ }
+
+ public static String escapeType(String s) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0, len = s.length(); i < len; i++) {
+ char c = s.charAt(i);
+ switch (c) {
+ case '$': sb.append("$24"); break;
+ case '.': sb.append("$2E"); break;
+ case '[': sb.append("$5B"); break;
+ case ';': sb.append("$3B"); break;
+ case '(': sb.append("$28"); break;
+ case ')': sb.append("$29"); break;
+ case '/': sb.append("$2F"); break;
+ default:
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/core/VisibilityPredicate.java b/cglib-and-asm/src/org/mockito/cglib/core/VisibilityPredicate.java
new file mode 100644
index 0000000..335cf72
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/core/VisibilityPredicate.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.core;
+
+import java.lang.reflect.*;
+
+import org.mockito.asm.Type;
+
+public class VisibilityPredicate implements Predicate {
+ private boolean protectedOk;
+ private String pkg;
+
+ public VisibilityPredicate(Class source, boolean protectedOk) {
+ this.protectedOk = protectedOk;
+ pkg = TypeUtils.getPackageName(Type.getType(source));
+ }
+
+ public boolean evaluate(Object arg) {
+ int mod = (arg instanceof Member) ? ((Member)arg).getModifiers() : ((Integer)arg).intValue();
+ if (Modifier.isPrivate(mod)) {
+ return false;
+ } else if (Modifier.isPublic(mod)) {
+ return true;
+ } else if (Modifier.isProtected(mod)) {
+ return protectedOk;
+ } else {
+ return pkg.equals(TypeUtils.getPackageName(Type.getType(((Member)arg).getDeclaringClass())));
+ }
+ }
+}
+
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/Callback.java b/cglib-and-asm/src/org/mockito/cglib/proxy/Callback.java
new file mode 100644
index 0000000..e0dcc5e
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/Callback.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+/**
+ * All callback interfaces used by {@link Enhancer} extend this interface.
+ * @see MethodInterceptor
+ * @see NoOp
+ * @see LazyLoader
+ * @see Dispatcher
+ * @see InvocationHandler
+ * @see FixedValue
+ */
+public interface Callback
+{
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackFilter.java b/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackFilter.java
new file mode 100644
index 0000000..b81e9b7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackFilter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * Map methods of subclasses generated by {@link Enhancer} to a particular
+ * callback. The type of the callbacks chosen for each method affects
+ * the bytecode generated for that method in the subclass, and cannot
+ * change for the life of the class.
+ */
+public interface CallbackFilter {
+ /**
+ * Map a method to a callback.
+ *
+ * @param method the intercepted method
+ * @param allMethods all the methods found on the instance. Don't mess with the contents of this list!!!
+ * @return the index into the array of callbacks (as specified by {@link Enhancer#setCallbacks}) to use for the method,
+ */
+ int accept(Method method, List<Method> allMethods);
+
+ /**
+ * The <code>CallbackFilter</code> in use affects which cached class
+ * the <code>Enhancer</code> will use, so this is a reminder that
+ * you should correctly implement <code>equals</code> and
+ * <code>hashCode</code> for custom <code>CallbackFilter</code>
+ * implementations in order to improve performance.
+ */
+ boolean equals(Object o);
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackGenerator.java b/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackGenerator.java
new file mode 100644
index 0000000..a0d4ae9
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackGenerator.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.util.List;
+
+import org.mockito.cglib.core.*;
+
+interface CallbackGenerator
+{
+ void generate(ClassEmitter ce, Context context, List methods) throws Exception;
+ void generateStatic(CodeEmitter e, Context context, List methods) throws Exception;
+
+ interface Context
+ {
+ ClassLoader getClassLoader();
+ CodeEmitter beginMethod(ClassEmitter ce, MethodInfo method);
+ int getOriginalModifiers(MethodInfo method);
+ int getIndex(MethodInfo method);
+ void emitCallback(CodeEmitter ce, int index);
+ Signature getImplSignature(MethodInfo method);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackHelper.java b/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackHelper.java
new file mode 100644
index 0000000..de5a4e8
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackHelper.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+import org.mockito.cglib.core.ReflectUtils;
+
+/**
+ * @version $Id: CallbackHelper.java,v 1.2 2004/06/24 21:15:20 herbyderby Exp $
+ */
+abstract public class CallbackHelper
+implements CallbackFilter
+{
+ private Map methodMap = new HashMap();
+ private List callbacks = new ArrayList();
+
+ public CallbackHelper(Class superclass, Class[] interfaces)
+ {
+ List methods = new ArrayList();
+ Enhancer.getMethods(superclass, interfaces, methods);
+ Map indexes = new HashMap();
+ for (int i = 0, size = methods.size(); i < size; i++) {
+ Method method = (Method)methods.get(i);
+ Object callback = getCallback(method);
+ if (callback == null)
+ throw new IllegalStateException("getCallback cannot return null");
+ boolean isCallback = callback instanceof Callback;
+ if (!(isCallback || (callback instanceof Class)))
+ throw new IllegalStateException("getCallback must return a Callback or a Class");
+ if (i > 0 && ((callbacks.get(i - 1) instanceof Callback) ^ isCallback))
+ throw new IllegalStateException("getCallback must return a Callback or a Class consistently for every Method");
+ Integer index = (Integer)indexes.get(callback);
+ if (index == null) {
+ index = new Integer(callbacks.size());
+ indexes.put(callback, index);
+ }
+ methodMap.put(method, index);
+ callbacks.add(callback);
+ }
+ }
+
+ abstract protected Object getCallback(Method method);
+
+ public Callback[] getCallbacks()
+ {
+ if (callbacks.size() == 0)
+ return new Callback[0];
+ if (callbacks.get(0) instanceof Callback) {
+ return (Callback[])callbacks.toArray(new Callback[callbacks.size()]);
+ } else {
+ throw new IllegalStateException("getCallback returned classes, not callbacks; call getCallbackTypes instead");
+ }
+ }
+
+ public Class[] getCallbackTypes()
+ {
+ if (callbacks.size() == 0)
+ return new Class[0];
+ if (callbacks.get(0) instanceof Callback) {
+ return ReflectUtils.getClasses(getCallbacks());
+ } else {
+ return (Class[])callbacks.toArray(new Class[callbacks.size()]);
+ }
+ }
+
+ public int accept(Method method, List<Method> allMethods)
+ {
+ return ((Integer)methodMap.get(method)).intValue();
+ }
+
+ public int hashCode()
+ {
+ return methodMap.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (o == null)
+ return false;
+ if (!(o instanceof CallbackHelper))
+ return false;
+ return methodMap.equals(((CallbackHelper)o).methodMap);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackInfo.java b/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackInfo.java
new file mode 100644
index 0000000..0f14be9
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/CallbackInfo.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import org.mockito.asm.Type;
+
+class CallbackInfo
+{
+ public static Type[] determineTypes(Class[] callbackTypes) {
+ Type[] types = new Type[callbackTypes.length];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = determineType(callbackTypes[i]);
+ }
+ return types;
+ }
+
+ public static Type[] determineTypes(Callback[] callbacks) {
+ Type[] types = new Type[callbacks.length];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = determineType(callbacks[i]);
+ }
+ return types;
+ }
+
+ public static CallbackGenerator[] getGenerators(Type[] callbackTypes) {
+ CallbackGenerator[] generators = new CallbackGenerator[callbackTypes.length];
+ for (int i = 0; i < generators.length; i++) {
+ generators[i] = getGenerator(callbackTypes[i]);
+ }
+ return generators;
+ }
+
+ //////////////////// PRIVATE ////////////////////
+
+ private Class cls;
+ private CallbackGenerator generator;
+ private Type type;
+
+ private static final CallbackInfo[] CALLBACKS = {
+ new CallbackInfo(NoOp.class, NoOpGenerator.INSTANCE),
+ new CallbackInfo(MethodInterceptor.class, MethodInterceptorGenerator.INSTANCE),
+ new CallbackInfo(InvocationHandler.class, InvocationHandlerGenerator.INSTANCE),
+ new CallbackInfo(LazyLoader.class, LazyLoaderGenerator.INSTANCE),
+ new CallbackInfo(Dispatcher.class, DispatcherGenerator.INSTANCE),
+ new CallbackInfo(FixedValue.class, FixedValueGenerator.INSTANCE),
+ new CallbackInfo(ProxyRefDispatcher.class, DispatcherGenerator.PROXY_REF_INSTANCE),
+ };
+
+ private CallbackInfo(Class cls, CallbackGenerator generator) {
+ this.cls = cls;
+ this.generator = generator;
+ type = Type.getType(cls);
+ }
+
+ private static Type determineType(Callback callback) {
+ if (callback == null) {
+ throw new IllegalStateException("Callback is null");
+ }
+ return determineType(callback.getClass());
+ }
+
+ private static Type determineType(Class callbackType) {
+ Class cur = null;
+ for (int i = 0; i < CALLBACKS.length; i++) {
+ CallbackInfo info = CALLBACKS[i];
+ if (info.cls.isAssignableFrom(callbackType)) {
+ if (cur != null) {
+ throw new IllegalStateException("Callback implements both " + cur + " and " + info.cls);
+ }
+ cur = info.cls;
+ }
+ }
+ if (cur == null) {
+ throw new IllegalStateException("Unknown callback type " + callbackType);
+ }
+ return Type.getType(cur);
+ }
+
+ private static CallbackGenerator getGenerator(Type callbackType) {
+ for (int i = 0; i < CALLBACKS.length; i++) {
+ CallbackInfo info = CALLBACKS[i];
+ if (info.type.equals(callbackType)) {
+ return info.generator;
+ }
+ }
+ throw new IllegalStateException("Unknown callback type " + callbackType);
+ }
+}
+
+
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/Dispatcher.java b/cglib-and-asm/src/org/mockito/cglib/proxy/Dispatcher.java
new file mode 100644
index 0000000..b301ea4
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/Dispatcher.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+/**
+ * Dispatching {@link Enhancer} callback. This is identical to the
+ * {@link LazyLoader} interface but needs to be separate so that <code>Enhancer</code>
+ * knows which type of code to generate.
+ */
+public interface Dispatcher extends Callback {
+ /**
+ * Return the object which the original method invocation should
+ * be dispatched. This method is called for <b>every</b> method invocation.
+ * @return an object that can invoke the method
+ */
+ Object loadObject() throws Exception;
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/DispatcherGenerator.java b/cglib-and-asm/src/org/mockito/cglib/proxy/DispatcherGenerator.java
new file mode 100644
index 0000000..ce9e34a
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/DispatcherGenerator.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.util.*;
+
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+class DispatcherGenerator implements CallbackGenerator {
+ public static final DispatcherGenerator INSTANCE =
+ new DispatcherGenerator(false);
+ public static final DispatcherGenerator PROXY_REF_INSTANCE =
+ new DispatcherGenerator(true);
+
+ private static final Type DISPATCHER =
+ TypeUtils.parseType("org.mockito.cglib.proxy.Dispatcher");
+ private static final Type PROXY_REF_DISPATCHER =
+ TypeUtils.parseType("org.mockito.cglib.proxy.ProxyRefDispatcher");
+ private static final Signature LOAD_OBJECT =
+ TypeUtils.parseSignature("Object loadObject()");
+ private static final Signature PROXY_REF_LOAD_OBJECT =
+ TypeUtils.parseSignature("Object loadObject(Object)");
+
+ private boolean proxyRef;
+
+ private DispatcherGenerator(boolean proxyRef) {
+ this.proxyRef = proxyRef;
+ }
+
+ public void generate(ClassEmitter ce, Context context, List methods) {
+ for (Iterator it = methods.iterator(); it.hasNext();) {
+ MethodInfo method = (MethodInfo)it.next();
+ if (!TypeUtils.isProtected(method.getModifiers())) {
+ CodeEmitter e = context.beginMethod(ce, method);
+ context.emitCallback(e, context.getIndex(method));
+ if (proxyRef) {
+ e.load_this();
+ e.invoke_interface(PROXY_REF_DISPATCHER, PROXY_REF_LOAD_OBJECT);
+ } else {
+ e.invoke_interface(DISPATCHER, LOAD_OBJECT);
+ }
+ e.checkcast(method.getClassInfo().getType());
+ e.load_args();
+ e.invoke(method);
+ e.return_value();
+ e.end_method();
+ }
+ }
+ }
+
+ public void generateStatic(CodeEmitter e, Context context, List methods) { }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/Enhancer.java b/cglib-and-asm/src/org/mockito/cglib/proxy/Enhancer.java
new file mode 100644
index 0000000..642db55
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/Enhancer.java
@@ -0,0 +1,1046 @@
+/*
+ * Copyright 2002,2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Label;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+/**
+ * Generates dynamic subclasses to enable method interception. This
+ * class started as a substitute for the standard Dynamic Proxy support
+ * included with JDK 1.3, but one that allowed the proxies to extend a
+ * concrete base class, in addition to implementing interfaces. The dynamically
+ * generated subclasses override the non-final methods of the superclass and
+ * have hooks which callback to user-defined interceptor
+ * implementations.
+ * <p>
+ * The original and most general callback type is the {@link MethodInterceptor}, which
+ * in AOP terms enables "around advice"--that is, you can invoke custom code both before
+ * and after the invocation of the "super" method. In addition you can modify the
+ * arguments before calling the super method, or not call it at all.
+ * <p>
+ * Although <code>MethodInterceptor</code> is generic enough to meet any
+ * interception need, it is often overkill. For simplicity and performance, additional
+ * specialized callback types, such as {@link LazyLoader} are also available.
+ * Often a single callback will be used per enhanced class, but you can control
+ * which callback is used on a per-method basis with a {@link CallbackFilter}.
+ * <p>
+ * The most common uses of this class are embodied in the static helper methods. For
+ * advanced needs, such as customizing the <code>ClassLoader</code> to use, you should create
+ * a new instance of <code>Enhancer</code>. Other classes within CGLIB follow a similar pattern.
+ * <p>
+ * All enhanced objects implement the {@link Factory} interface, unless {@link #setUseFactory} is
+ * used to explicitly disable this feature. The <code>Factory</code> interface provides an API
+ * to change the callbacks of an existing object, as well as a faster and easier way to create
+ * new instances of the same type.
+ * <p>
+ * For an almost drop-in replacement for
+ * <code>java.lang.reflect.Proxy</code>, see the {@link Proxy} class.
+ */
+public class Enhancer extends AbstractClassGenerator
+{
+ private static final CallbackFilter ALL_ZERO = new CallbackFilter(){
+ public int accept(Method method, List<Method> allMethods) {
+ return 0;
+ }
+ };
+
+ private static final Source SOURCE = new Source(Enhancer.class.getName());
+ private static final EnhancerKey KEY_FACTORY =
+ (EnhancerKey)KeyFactory.create(EnhancerKey.class);
+
+ private static final String BOUND_FIELD = "CGLIB$BOUND";
+ private static final String THREAD_CALLBACKS_FIELD = "CGLIB$THREAD_CALLBACKS";
+ private static final String STATIC_CALLBACKS_FIELD = "CGLIB$STATIC_CALLBACKS";
+ private static final String SET_THREAD_CALLBACKS_NAME = "CGLIB$SET_THREAD_CALLBACKS";
+ private static final String SET_STATIC_CALLBACKS_NAME = "CGLIB$SET_STATIC_CALLBACKS";
+ private static final String CONSTRUCTED_FIELD = "CGLIB$CONSTRUCTED";
+
+ private static final Type FACTORY =
+ TypeUtils.parseType("org.mockito.cglib.proxy.Factory");
+ private static final Type ILLEGAL_STATE_EXCEPTION =
+ TypeUtils.parseType("IllegalStateException");
+ private static final Type ILLEGAL_ARGUMENT_EXCEPTION =
+ TypeUtils.parseType("IllegalArgumentException");
+ private static final Type THREAD_LOCAL =
+ TypeUtils.parseType("ThreadLocal");
+ private static final Type CALLBACK =
+ TypeUtils.parseType("org.mockito.cglib.proxy.Callback");
+ private static final Type CALLBACK_ARRAY =
+ Type.getType(Callback[].class);
+ private static final Signature CSTRUCT_NULL =
+ TypeUtils.parseConstructor("");
+ private static final Signature SET_THREAD_CALLBACKS =
+ new Signature(SET_THREAD_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
+ private static final Signature SET_STATIC_CALLBACKS =
+ new Signature(SET_STATIC_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
+ private static final Signature NEW_INSTANCE =
+ new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK_ARRAY });
+ private static final Signature MULTIARG_NEW_INSTANCE =
+ new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{
+ Constants.TYPE_CLASS_ARRAY,
+ Constants.TYPE_OBJECT_ARRAY,
+ CALLBACK_ARRAY,
+ });
+ private static final Signature SINGLE_NEW_INSTANCE =
+ new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK });
+ private static final Signature SET_CALLBACK =
+ new Signature("setCallback", Type.VOID_TYPE, new Type[]{ Type.INT_TYPE, CALLBACK });
+ private static final Signature GET_CALLBACK =
+ new Signature("getCallback", CALLBACK, new Type[]{ Type.INT_TYPE });
+ private static final Signature SET_CALLBACKS =
+ new Signature("setCallbacks", Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
+ private static final Signature GET_CALLBACKS =
+ new Signature("getCallbacks", CALLBACK_ARRAY, new Type[0]);
+ private static final Signature THREAD_LOCAL_GET =
+ TypeUtils.parseSignature("Object get()");
+ private static final Signature THREAD_LOCAL_SET =
+ TypeUtils.parseSignature("void set(Object)");
+ private static final Signature BIND_CALLBACKS =
+ TypeUtils.parseSignature("void CGLIB$BIND_CALLBACKS(Object)");
+
+ /** Internal interface, only public due to ClassLoader issues. */
+ public interface EnhancerKey {
+ public Object newInstance(String type,
+ String[] interfaces,
+ CallbackFilter filter,
+ Type[] callbackTypes,
+ boolean useFactory,
+ boolean interceptDuringConstruction,
+ Long serialVersionUID);
+ }
+
+ private Class[] interfaces;
+ private CallbackFilter filter;
+ private Callback[] callbacks;
+ private Type[] callbackTypes;
+ private boolean classOnly;
+ private Class superclass;
+ private Class[] argumentTypes;
+ private Object[] arguments;
+ private boolean useFactory = true;
+ private Long serialVersionUID;
+ private boolean interceptDuringConstruction = true;
+
+ /**
+ * Create a new <code>Enhancer</code>. A new <code>Enhancer</code>
+ * object should be used for each generated object, and should not
+ * be shared across threads. To create additional instances of a
+ * generated class, use the <code>Factory</code> interface.
+ * @see Factory
+ */
+ public Enhancer() {
+ super(SOURCE);
+ }
+
+ /**
+ * Set the class which the generated class will extend. As a convenience,
+ * if the supplied superclass is actually an interface, <code>setInterfaces</code>
+ * will be called with the appropriate argument instead.
+ * A non-interface argument must not be declared as final, and must have an
+ * accessible constructor.
+ * @param superclass class to extend or interface to implement
+ * @see #setInterfaces(Class[])
+ */
+ public void setSuperclass(Class superclass) {
+ if (superclass != null && superclass.isInterface()) {
+ setInterfaces(new Class[]{ superclass });
+ } else if (superclass != null && superclass.equals(Object.class)) {
+ // affects choice of ClassLoader
+ this.superclass = null;
+ } else {
+ this.superclass = superclass;
+ }
+ }
+
+ /**
+ * Set the interfaces to implement. The <code>Factory</code> interface will
+ * always be implemented regardless of what is specified here.
+ * @param interfaces array of interfaces to implement, or null
+ * @see Factory
+ */
+ public void setInterfaces(Class[] interfaces) {
+ this.interfaces = interfaces;
+ }
+
+ /**
+ * Set the {@link CallbackFilter} used to map the generated class' methods
+ * to a particular callback index.
+ * New object instances will always use the same mapping, but may use different
+ * actual callback objects.
+ * @param filter the callback filter to use when generating a new class
+ * @see #setCallbacks
+ */
+ public void setCallbackFilter(CallbackFilter filter) {
+ this.filter = filter;
+ }
+
+
+ /**
+ * Set the single {@link Callback} to use.
+ * Ignored if you use {@link #createClass}.
+ * @param callback the callback to use for all methods
+ * @see #setCallbacks
+ */
+ public void setCallback(final Callback callback) {
+ setCallbacks(new Callback[]{ callback });
+ }
+
+ /**
+ * Set the array of callbacks to use.
+ * Ignored if you use {@link #createClass}.
+ * You must use a {@link CallbackFilter} to specify the index into this
+ * array for each method in the proxied class.
+ * @param callbacks the callback array
+ * @see #setCallbackFilter
+ * @see #setCallback
+ */
+ public void setCallbacks(Callback[] callbacks) {
+ if (callbacks != null && callbacks.length == 0) {
+ throw new IllegalArgumentException("Array cannot be empty");
+ }
+ this.callbacks = callbacks;
+ }
+
+ /**
+ * Set whether the enhanced object instances should implement
+ * the {@link Factory} interface.
+ * This was added for tools that need for proxies to be more
+ * indistinguishable from their targets. Also, in some cases it may
+ * be necessary to disable the <code>Factory</code> interface to
+ * prevent code from changing the underlying callbacks.
+ * @param useFactory whether to implement <code>Factory</code>; default is <code>true</code>
+ */
+ public void setUseFactory(boolean useFactory) {
+ this.useFactory = useFactory;
+ }
+
+ /**
+ * Set whether methods called from within the proxy's constructer
+ * will be intercepted. The default value is true. Unintercepted methods
+ * will call the method of the proxy's base class, if it exists.
+ * @param interceptDuringConstruction whether to intercept methods called from the constructor
+ */
+ public void setInterceptDuringConstruction(boolean interceptDuringConstruction) {
+ this.interceptDuringConstruction = interceptDuringConstruction;
+ }
+
+ /**
+ * Set the single type of {@link Callback} to use.
+ * This may be used instead of {@link #setCallback} when calling
+ * {@link #createClass}, since it may not be possible to have
+ * an array of actual callback instances.
+ * @param callbackType the type of callback to use for all methods
+ * @see #setCallbackTypes
+ */
+ public void setCallbackType(Class callbackType) {
+ setCallbackTypes(new Class[]{ callbackType });
+ }
+
+ /**
+ * Set the array of callback types to use.
+ * This may be used instead of {@link #setCallbacks} when calling
+ * {@link #createClass}, since it may not be possible to have
+ * an array of actual callback instances.
+ * You must use a {@link CallbackFilter} to specify the index into this
+ * array for each method in the proxied class.
+ * @param callbackTypes the array of callback types
+ */
+ public void setCallbackTypes(Class[] callbackTypes) {
+ if (callbackTypes != null && callbackTypes.length == 0) {
+ throw new IllegalArgumentException("Array cannot be empty");
+ }
+ this.callbackTypes = CallbackInfo.determineTypes(callbackTypes);
+ }
+
+ /**
+ * Generate a new class if necessary and uses the specified
+ * callbacks (if any) to create a new object instance.
+ * Uses the no-arg constructor of the superclass.
+ * @return a new instance
+ */
+ public Object create() {
+ classOnly = false;
+ argumentTypes = null;
+ return createHelper();
+ }
+
+ /**
+ * Generate a new class if necessary and uses the specified
+ * callbacks (if any) to create a new object instance.
+ * Uses the constructor of the superclass matching the <code>argumentTypes</code>
+ * parameter, with the given arguments.
+ * @param argumentTypes constructor signature
+ * @param arguments compatible wrapped arguments to pass to constructor
+ * @return a new instance
+ */
+ public Object create(Class[] argumentTypes, Object[] arguments) {
+ classOnly = false;
+ if (argumentTypes == null || arguments == null || argumentTypes.length != arguments.length) {
+ throw new IllegalArgumentException("Arguments must be non-null and of equal length");
+ }
+ this.argumentTypes = argumentTypes;
+ this.arguments = arguments;
+ return createHelper();
+ }
+
+ /**
+ * Generate a new class if necessary and return it without creating a new instance.
+ * This ignores any callbacks that have been set.
+ * To create a new instance you will have to use reflection, and methods
+ * called during the constructor will not be intercepted. To avoid this problem,
+ * use the multi-arg <code>create</code> method.
+ * @see #create(Class[], Object[])
+ */
+ public Class createClass() {
+ classOnly = true;
+ return (Class)createHelper();
+ }
+
+ /**
+ * Insert a static serialVersionUID field into the generated class.
+ * @param sUID the field value, or null to avoid generating field.
+ */
+ public void setSerialVersionUID(Long sUID) {
+ serialVersionUID = sUID;
+ }
+
+ private void validate() {
+ if (classOnly ^ (callbacks == null)) {
+ if (classOnly) {
+ throw new IllegalStateException("createClass does not accept callbacks");
+ } else {
+ throw new IllegalStateException("Callbacks are required");
+ }
+ }
+ if (classOnly && (callbackTypes == null)) {
+ throw new IllegalStateException("Callback types are required");
+ }
+ if (callbacks != null && callbackTypes != null) {
+ if (callbacks.length != callbackTypes.length) {
+ throw new IllegalStateException("Lengths of callback and callback types array must be the same");
+ }
+ Type[] check = CallbackInfo.determineTypes(callbacks);
+ for (int i = 0; i < check.length; i++) {
+ if (!check[i].equals(callbackTypes[i])) {
+ throw new IllegalStateException("Callback " + check[i] + " is not assignable to " + callbackTypes[i]);
+ }
+ }
+ } else if (callbacks != null) {
+ callbackTypes = CallbackInfo.determineTypes(callbacks);
+ }
+ if (filter == null) {
+ if (callbackTypes.length > 1) {
+ throw new IllegalStateException("Multiple callback types possible but no filter specified");
+ }
+ filter = ALL_ZERO;
+ }
+ if (interfaces != null) {
+ for (int i = 0; i < interfaces.length; i++) {
+ if (interfaces[i] == null) {
+ throw new IllegalStateException("Interfaces cannot be null");
+ }
+ if (!interfaces[i].isInterface()) {
+ throw new IllegalStateException(interfaces[i] + " is not an interface");
+ }
+ }
+ }
+ }
+
+ private Object createHelper() {
+ validate();
+ if (superclass != null) {
+ setNamePrefix(superclass.getName());
+ } else if (interfaces != null) {
+ setNamePrefix(interfaces[ReflectUtils.findPackageProtected(interfaces)].getName());
+ }
+ return super.create(KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
+ ReflectUtils.getNames(interfaces),
+ filter,
+ callbackTypes,
+ useFactory,
+ interceptDuringConstruction,
+ serialVersionUID));
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ if (superclass != null) {
+ return superclass.getClassLoader();
+ } else if (interfaces != null) {
+ return interfaces[0].getClassLoader();
+ } else {
+ return null;
+ }
+ }
+
+ private Signature rename(Signature sig, int index) {
+ return new Signature("CGLIB$" + sig.getName() + "$" + index,
+ sig.getDescriptor());
+ }
+
+ /**
+ * Finds all of the methods that will be extended by an
+ * Enhancer-generated class using the specified superclass and
+ * interfaces. This can be useful in building a list of Callback
+ * objects. The methods are added to the end of the given list. Due
+ * to the subclassing nature of the classes generated by Enhancer,
+ * the methods are guaranteed to be non-static, non-final, and
+ * non-private. Each method signature will only occur once, even if
+ * it occurs in multiple classes.
+ * @param superclass the class that will be extended, or null
+ * @param interfaces the list of interfaces that will be implemented, or null
+ * @param methods the list into which to copy the applicable methods
+ */
+ public static void getMethods(Class superclass, Class[] interfaces, List methods)
+ {
+ getMethods(superclass, interfaces, methods, null, null);
+ }
+
+ private static void getMethods(Class superclass, Class[] interfaces, List methods, List interfaceMethods, Set forcePublic)
+ {
+ ReflectUtils.addAllMethods(superclass, methods);
+ List target = (interfaceMethods != null) ? interfaceMethods : methods;
+ if (interfaces != null) {
+ for (int i = 0; i < interfaces.length; i++) {
+ if (interfaces[i] != Factory.class) {
+ ReflectUtils.addAllMethods(interfaces[i], target);
+ }
+ }
+ }
+ if (interfaceMethods != null) {
+ if (forcePublic != null) {
+ forcePublic.addAll(MethodWrapper.createSet(interfaceMethods));
+ }
+ methods.addAll(interfaceMethods);
+ }
+ CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_STATIC));
+ CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true));
+ CollectionUtils.filter(methods, new DuplicatesPredicate());
+ CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_FINAL));
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ Class sc = (superclass == null) ? Object.class : superclass;
+
+ if (TypeUtils.isFinal(sc.getModifiers()))
+ throw new IllegalArgumentException("Cannot subclass final class " + sc);
+ List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors()));
+ filterConstructors(sc, constructors);
+
+ // Order is very important: must add superclass, then
+ // its superclass chain, then each interface and
+ // its superinterfaces.
+ List actualMethods = new ArrayList();
+ List interfaceMethods = new ArrayList();
+ final Set forcePublic = new HashSet();
+ getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic);
+
+ List methods = CollectionUtils.transform(actualMethods, new Transformer() {
+ public Object transform(Object value) {
+ Method method = (Method)value;
+ int modifiers = Constants.ACC_FINAL
+ | (method.getModifiers()
+ & ~Constants.ACC_ABSTRACT
+ & ~Constants.ACC_NATIVE
+ & ~Constants.ACC_SYNCHRONIZED);
+ if (forcePublic.contains(MethodWrapper.create(method))) {
+ modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC;
+ }
+ return ReflectUtils.getMethodInfo(method, modifiers);
+ }
+ });
+
+ ClassEmitter e = new ClassEmitter(v);
+ e.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ Type.getType(sc),
+ (useFactory ?
+ TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) :
+ TypeUtils.getTypes(interfaces)),
+ Constants.SOURCE_FILE);
+ List constructorInfo = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance());
+
+ e.declare_field(Constants.ACC_PRIVATE, BOUND_FIELD, Type.BOOLEAN_TYPE, null);
+ if (!interceptDuringConstruction) {
+ e.declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD, Type.BOOLEAN_TYPE, null);
+ }
+ e.declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null);
+ e.declare_field(Constants.PRIVATE_FINAL_STATIC, STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null);
+ if (serialVersionUID != null) {
+ e.declare_field(Constants.PRIVATE_FINAL_STATIC, Constants.SUID_FIELD_NAME, Type.LONG_TYPE, serialVersionUID);
+ }
+
+ for (int i = 0; i < callbackTypes.length; i++) {
+ e.declare_field(Constants.ACC_PRIVATE, getCallbackField(i), callbackTypes[i], null);
+ }
+
+ emitMethods(e, methods, actualMethods);
+ emitConstructors(e, constructorInfo);
+ emitSetThreadCallbacks(e);
+ emitSetStaticCallbacks(e);
+ emitBindCallbacks(e);
+
+ if (useFactory) {
+ int[] keys = getCallbackKeys();
+ emitNewInstanceCallbacks(e);
+ emitNewInstanceCallback(e);
+ emitNewInstanceMultiarg(e, constructorInfo);
+ emitGetCallback(e, keys);
+ emitSetCallback(e, keys);
+ emitGetCallbacks(e);
+ emitSetCallbacks(e);
+ }
+
+ e.end_class();
+ }
+
+ /**
+ * Filter the list of constructors from the superclass. The
+ * constructors which remain will be included in the generated
+ * class. The default implementation is to filter out all private
+ * constructors, but subclasses may extend Enhancer to override this
+ * behavior.
+ * @param sc the superclass
+ * @param constructors the list of all declared constructors from the superclass
+ * @throws IllegalArgumentException if there are no non-private constructors
+ */
+ protected void filterConstructors(Class sc, List constructors) {
+ CollectionUtils.filter(constructors, new VisibilityPredicate(sc, true));
+ if (constructors.size() == 0)
+ throw new IllegalArgumentException("No visible constructors in " + sc);
+ }
+
+ protected Object firstInstance(Class type) throws Exception {
+ if (classOnly) {
+ return type;
+ } else {
+ return createUsingReflection(type);
+ }
+ }
+
+ protected Object nextInstance(Object instance) {
+ Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass();
+ if (classOnly) {
+ return protoclass;
+ } else if (instance instanceof Factory) {
+ if (argumentTypes != null) {
+ return ((Factory)instance).newInstance(argumentTypes, arguments, callbacks);
+ } else {
+ return ((Factory)instance).newInstance(callbacks);
+ }
+ } else {
+ return createUsingReflection(protoclass);
+ }
+ }
+
+ /**
+ * Call this method to register the {@link Callback} array to use before
+ * creating a new instance of the generated class via reflection. If you are using
+ * an instance of <code>Enhancer</code> or the {@link Factory} interface to create
+ * new instances, this method is unnecessary. Its primary use is for when you want to
+ * cache and reuse a generated class yourself, and the generated class does
+ * <i>not</i> implement the {@link Factory} interface.
+ * <p>
+ * Note that this method only registers the callbacks on the current thread.
+ * If you want to register callbacks for instances created by multiple threads,
+ * use {@link #registerStaticCallbacks}.
+ * <p>
+ * The registered callbacks are overwritten and subsequently cleared
+ * when calling any of the <code>create</code> methods (such as
+ * {@link #create}), or any {@link Factory} <code>newInstance</code> method.
+ * Otherwise they are <i>not</i> cleared, and you should be careful to set them
+ * back to <code>null</code> after creating new instances via reflection if
+ * memory leakage is a concern.
+ * @param generatedClass a class previously created by {@link Enhancer}
+ * @param callbacks the array of callbacks to use when instances of the generated
+ * class are created
+ * @see #setUseFactory
+ */
+ public static void registerCallbacks(Class generatedClass, Callback[] callbacks) {
+ setThreadCallbacks(generatedClass, callbacks);
+ }
+
+ /**
+ * Similar to {@link #registerCallbacks}, but suitable for use
+ * when multiple threads will be creating instances of the generated class.
+ * The thread-level callbacks will always override the static callbacks.
+ * Static callbacks are never cleared.
+ * @param generatedClass a class previously created by {@link Enhancer}
+ * @param callbacks the array of callbacks to use when instances of the generated
+ * class are created
+ */
+ public static void registerStaticCallbacks(Class generatedClass, Callback[] callbacks) {
+ setCallbacksHelper(generatedClass, callbacks, SET_STATIC_CALLBACKS_NAME);
+ }
+
+ /**
+ * Determine if a class was generated using <code>Enhancer</code>.
+ * @param type any class
+ * @return whether the class was generated using <code>Enhancer</code>
+ */
+ public static boolean isEnhanced(Class type) {
+ try {
+ getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME);
+ return true;
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static void setThreadCallbacks(Class type, Callback[] callbacks) {
+ setCallbacksHelper(type, callbacks, SET_THREAD_CALLBACKS_NAME);
+ }
+
+ private static void setCallbacksHelper(Class type, Callback[] callbacks, String methodName) {
+ // TODO: optimize
+ try {
+ Method setter = getCallbacksSetter(type, methodName);
+ setter.invoke(null, new Object[]{ callbacks });
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException(type + " is not an enhanced class");
+ } catch (IllegalAccessException e) {
+ throw new CodeGenerationException(e);
+ } catch (InvocationTargetException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ private static Method getCallbacksSetter(Class type, String methodName) throws NoSuchMethodException {
+ return type.getDeclaredMethod(methodName, new Class[]{ Callback[].class });
+ }
+
+ private Object createUsingReflection(Class type) {
+ setThreadCallbacks(type, callbacks);
+ try{
+
+ if (argumentTypes != null) {
+
+ return ReflectUtils.newInstance(type, argumentTypes, arguments);
+
+ } else {
+
+ return ReflectUtils.newInstance(type);
+
+ }
+ }finally{
+ // clear thread callbacks to allow them to be gc'd
+ setThreadCallbacks(type, null);
+ }
+ }
+
+ /**
+ * Helper method to create an intercepted object.
+ * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
+ * instead of this static method.
+ * @param type class to extend or interface to implement
+ * @param callback the callback to use for all methods
+ */
+ public static Object create(Class type, Callback callback) {
+ Enhancer e = new Enhancer();
+ e.setSuperclass(type);
+ e.setCallback(callback);
+ return e.create();
+ }
+
+ /**
+ * Helper method to create an intercepted object.
+ * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
+ * instead of this static method.
+ * @param type class to extend or interface to implement
+ * @param interfaces array of interfaces to implement, or null
+ * @param callback the callback to use for all methods
+ */
+ public static Object create(Class superclass, Class interfaces[], Callback callback) {
+ Enhancer e = new Enhancer();
+ e.setSuperclass(superclass);
+ e.setInterfaces(interfaces);
+ e.setCallback(callback);
+ return e.create();
+ }
+
+ /**
+ * Helper method to create an intercepted object.
+ * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
+ * instead of this static method.
+ * @param type class to extend or interface to implement
+ * @param interfaces array of interfaces to implement, or null
+ * @param filter the callback filter to use when generating a new class
+ * @param callbacks callback implementations to use for the enhanced object
+ */
+ public static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks) {
+ Enhancer e = new Enhancer();
+ e.setSuperclass(superclass);
+ e.setInterfaces(interfaces);
+ e.setCallbackFilter(filter);
+ e.setCallbacks(callbacks);
+ return e.create();
+ }
+
+ private void emitConstructors(ClassEmitter ce, List constructors) {
+ boolean seenNull = false;
+ for (Iterator it = constructors.iterator(); it.hasNext();) {
+ MethodInfo constructor = (MethodInfo)it.next();
+ CodeEmitter e = EmitUtils.begin_method(ce, constructor, Constants.ACC_PUBLIC);
+ e.load_this();
+ e.dup();
+ e.load_args();
+ Signature sig = constructor.getSignature();
+ seenNull = seenNull || sig.getDescriptor().equals("()V");
+ e.super_invoke_constructor(sig);
+ e.invoke_static_this(BIND_CALLBACKS);
+ if (!interceptDuringConstruction) {
+ e.load_this();
+ e.push(1);
+ e.putfield(CONSTRUCTED_FIELD);
+ }
+ e.return_value();
+ e.end_method();
+ }
+ if (!classOnly && !seenNull && arguments == null)
+ throw new IllegalArgumentException("Superclass has no null constructors but no arguments were given");
+ }
+
+ private int[] getCallbackKeys() {
+ int[] keys = new int[callbackTypes.length];
+ for (int i = 0; i < callbackTypes.length; i++) {
+ keys[i] = i;
+ }
+ return keys;
+ }
+
+ private void emitGetCallback(ClassEmitter ce, int[] keys) {
+ final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACK, null);
+ e.load_this();
+ e.invoke_static_this(BIND_CALLBACKS);
+ e.load_this();
+ e.load_arg(0);
+ e.process_switch(keys, new ProcessSwitchCallback() {
+ public void processCase(int key, Label end) {
+ e.getfield(getCallbackField(key));
+ e.goTo(end);
+ }
+ public void processDefault() {
+ e.pop(); // stack height
+ e.aconst_null();
+ }
+ });
+ e.return_value();
+ e.end_method();
+ }
+
+ private void emitSetCallback(ClassEmitter ce, int[] keys) {
+ final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACK, null);
+ e.load_arg(0);
+ e.process_switch(keys, new ProcessSwitchCallback() {
+ public void processCase(int key, Label end) {
+ e.load_this();
+ e.load_arg(1);
+ e.checkcast(callbackTypes[key]);
+ e.putfield(getCallbackField(key));
+ e.goTo(end);
+ }
+ public void processDefault() {
+ // TODO: error?
+ }
+ });
+ e.return_value();
+ e.end_method();
+ }
+
+ private void emitSetCallbacks(ClassEmitter ce) {
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACKS, null);
+ e.load_this();
+ e.load_arg(0);
+ for (int i = 0; i < callbackTypes.length; i++) {
+ e.dup2();
+ e.aaload(i);
+ e.checkcast(callbackTypes[i]);
+ e.putfield(getCallbackField(i));
+ }
+ e.return_value();
+ e.end_method();
+ }
+
+ private void emitGetCallbacks(ClassEmitter ce) {
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACKS, null);
+ e.load_this();
+ e.invoke_static_this(BIND_CALLBACKS);
+ e.load_this();
+ e.push(callbackTypes.length);
+ e.newarray(CALLBACK);
+ for (int i = 0; i < callbackTypes.length; i++) {
+ e.dup();
+ e.push(i);
+ e.load_this();
+ e.getfield(getCallbackField(i));
+ e.aastore();
+ }
+ e.return_value();
+ e.end_method();
+ }
+
+ private void emitNewInstanceCallbacks(ClassEmitter ce) {
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null);
+ e.load_arg(0);
+ e.invoke_static_this(SET_THREAD_CALLBACKS);
+ emitCommonNewInstance(e);
+ }
+
+ private void emitCommonNewInstance(CodeEmitter e) {
+ e.new_instance_this();
+ e.dup();
+ e.invoke_constructor_this();
+ e.aconst_null();
+ e.invoke_static_this(SET_THREAD_CALLBACKS);
+ e.return_value();
+ e.end_method();
+ }
+
+ private void emitNewInstanceCallback(ClassEmitter ce) {
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SINGLE_NEW_INSTANCE, null);
+ switch (callbackTypes.length) {
+ case 0:
+ // TODO: make sure Callback is null
+ break;
+ case 1:
+ // for now just make a new array; TODO: optimize
+ e.push(1);
+ e.newarray(CALLBACK);
+ e.dup();
+ e.push(0);
+ e.load_arg(0);
+ e.aastore();
+ e.invoke_static_this(SET_THREAD_CALLBACKS);
+ break;
+ default:
+ e.throw_exception(ILLEGAL_STATE_EXCEPTION, "More than one callback object required");
+ }
+ emitCommonNewInstance(e);
+ }
+
+ private void emitNewInstanceMultiarg(ClassEmitter ce, List constructors) {
+ final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, MULTIARG_NEW_INSTANCE, null);
+ e.load_arg(2);
+ e.invoke_static_this(SET_THREAD_CALLBACKS);
+ e.new_instance_this();
+ e.dup();
+ e.load_arg(0);
+ EmitUtils.constructor_switch(e, constructors, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ MethodInfo constructor = (MethodInfo)key;
+ Type types[] = constructor.getSignature().getArgumentTypes();
+ for (int i = 0; i < types.length; i++) {
+ e.load_arg(1);
+ e.push(i);
+ e.aaload();
+ e.unbox(types[i]);
+ }
+ e.invoke_constructor_this(constructor.getSignature());
+ e.goTo(end);
+ }
+ public void processDefault() {
+ e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Constructor not found");
+ }
+ });
+ e.aconst_null();
+ e.invoke_static_this(SET_THREAD_CALLBACKS);
+ e.return_value();
+ e.end_method();
+ }
+
+ private void emitMethods(final ClassEmitter ce, List methods, List actualMethods) {
+ CallbackGenerator[] generators = CallbackInfo.getGenerators(callbackTypes);
+
+ Map groups = new HashMap();
+ final Map indexes = new HashMap();
+ final Map originalModifiers = new HashMap();
+ final Map positions = CollectionUtils.getIndexMap(methods);
+
+ Iterator it1 = methods.iterator();
+ Iterator it2 = (actualMethods != null) ? actualMethods.iterator() : null;
+
+ while (it1.hasNext()) {
+ MethodInfo method = (MethodInfo)it1.next();
+ Method actualMethod = (it2 != null) ? (Method)it2.next() : null;
+ int index = filter.accept(actualMethod, actualMethods);
+ if (index >= callbackTypes.length) {
+ throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index);
+ }
+ originalModifiers.put(method, new Integer((actualMethod != null) ? actualMethod.getModifiers() : method.getModifiers()));
+ indexes.put(method, new Integer(index));
+ List group = (List)groups.get(generators[index]);
+ if (group == null) {
+ groups.put(generators[index], group = new ArrayList(methods.size()));
+ }
+ group.add(method);
+ }
+
+ Set seenGen = new HashSet();
+ CodeEmitter se = ce.getStaticHook();
+ se.new_instance(THREAD_LOCAL);
+ se.dup();
+ se.invoke_constructor(THREAD_LOCAL, CSTRUCT_NULL);
+ se.putfield(THREAD_CALLBACKS_FIELD);
+
+ final Object[] state = new Object[1];
+ CallbackGenerator.Context context = new CallbackGenerator.Context() {
+ public ClassLoader getClassLoader() {
+ return Enhancer.this.getClassLoader();
+ }
+ public int getOriginalModifiers(MethodInfo method) {
+ return ((Integer)originalModifiers.get(method)).intValue();
+ }
+ public int getIndex(MethodInfo method) {
+ return ((Integer)indexes.get(method)).intValue();
+ }
+ public void emitCallback(CodeEmitter e, int index) {
+ emitCurrentCallback(e, index);
+ }
+ public Signature getImplSignature(MethodInfo method) {
+ return rename(method.getSignature(), ((Integer)positions.get(method)).intValue());
+ }
+ public CodeEmitter beginMethod(ClassEmitter ce, MethodInfo method) {
+ CodeEmitter e = EmitUtils.begin_method(ce, method);
+ if (!interceptDuringConstruction &&
+ !TypeUtils.isAbstract(method.getModifiers())) {
+ Label constructed = e.make_label();
+ e.load_this();
+ e.getfield(CONSTRUCTED_FIELD);
+ e.if_jump(e.NE, constructed);
+ e.load_this();
+ e.load_args();
+ e.super_invoke();
+ e.return_value();
+ e.mark(constructed);
+ }
+ return e;
+ }
+ };
+ for (int i = 0; i < callbackTypes.length; i++) {
+ CallbackGenerator gen = generators[i];
+ if (!seenGen.contains(gen)) {
+ seenGen.add(gen);
+ final List fmethods = (List)groups.get(gen);
+ if (fmethods != null) {
+ try {
+ gen.generate(ce, context, fmethods);
+ gen.generateStatic(se, context, fmethods);
+ } catch (RuntimeException x) {
+ throw x;
+ } catch (Exception x) {
+ throw new CodeGenerationException(x);
+ }
+ }
+ }
+ }
+ se.return_value();
+ se.end_method();
+ }
+
+ private void emitSetThreadCallbacks(ClassEmitter ce) {
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
+ SET_THREAD_CALLBACKS,
+ null);
+ e.getfield(THREAD_CALLBACKS_FIELD);
+ e.load_arg(0);
+ e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
+ e.return_value();
+ e.end_method();
+ }
+
+ private void emitSetStaticCallbacks(ClassEmitter ce) {
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
+ SET_STATIC_CALLBACKS,
+ null);
+ e.load_arg(0);
+ e.putfield(STATIC_CALLBACKS_FIELD);
+ e.return_value();
+ e.end_method();
+ }
+
+ private void emitCurrentCallback(CodeEmitter e, int index) {
+ e.load_this();
+ e.getfield(getCallbackField(index));
+ e.dup();
+ Label end = e.make_label();
+ e.ifnonnull(end);
+ e.pop(); // stack height
+ e.load_this();
+ e.invoke_static_this(BIND_CALLBACKS);
+ e.load_this();
+ e.getfield(getCallbackField(index));
+ e.mark(end);
+ }
+
+ private void emitBindCallbacks(ClassEmitter ce) {
+ CodeEmitter e = ce.begin_method(Constants.PRIVATE_FINAL_STATIC,
+ BIND_CALLBACKS,
+ null);
+ Local me = e.make_local();
+ e.load_arg(0);
+ e.checkcast_this();
+ e.store_local(me);
+
+ Label end = e.make_label();
+ e.load_local(me);
+ e.getfield(BOUND_FIELD);
+ e.if_jump(e.NE, end);
+ e.load_local(me);
+ e.push(1);
+ e.putfield(BOUND_FIELD);
+
+ e.getfield(THREAD_CALLBACKS_FIELD);
+ e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET);
+ e.dup();
+ Label found_callback = e.make_label();
+ e.ifnonnull(found_callback);
+ e.pop();
+
+ e.getfield(STATIC_CALLBACKS_FIELD);
+ e.dup();
+ e.ifnonnull(found_callback);
+ e.pop();
+ e.goTo(end);
+
+ e.mark(found_callback);
+ e.checkcast(CALLBACK_ARRAY);
+ e.load_local(me);
+ e.swap();
+ for (int i = callbackTypes.length - 1; i >= 0; i--) {
+ if (i != 0) {
+ e.dup2();
+ }
+ e.aaload(i);
+ e.checkcast(callbackTypes[i]);
+ e.putfield(getCallbackField(i));
+ }
+
+ e.mark(end);
+ e.return_value();
+ e.end_method();
+ }
+
+ private static String getCallbackField(int index) {
+ return "CGLIB$CALLBACK_" + index;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/Factory.java b/cglib-and-asm/src/org/mockito/cglib/proxy/Factory.java
new file mode 100644
index 0000000..033a5fe
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/Factory.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2002,2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mockito.cglib.proxy;
+
+/**
+ * All enhanced instances returned by the {@link Enhancer} class implement this interface.
+ * Using this interface for new instances is faster than going through the <code>Enhancer</code>
+ * interface or using reflection. In addition, to intercept methods called during
+ * object construction you <b>must</b> use these methods instead of reflection.
+ * @author Juozas Baliuka <a href="mailto:baliuka@mwm.lt">baliuka@mwm.lt</a>
+ * @version $Id: Factory.java,v 1.13 2004/06/24 21:15:20 herbyderby Exp $
+ */
+public interface Factory {
+ /**
+ * Creates new instance of the same type, using the no-arg constructor.
+ * The class of this object must have been created using a single Callback type.
+ * If multiple callbacks are required an exception will be thrown.
+ * @param callback the new interceptor to use
+ * @return new instance of the same type
+ */
+ Object newInstance(Callback callback);
+
+ /**
+ * Creates new instance of the same type, using the no-arg constructor.
+ * @param callbacks the new callbacks(s) to use
+ * @return new instance of the same type
+ */
+ Object newInstance(Callback[] callbacks);
+
+ /**
+ * Creates a new instance of the same type, using the constructor
+ * matching the given signature.
+ * @param types the constructor argument types
+ * @param args the constructor arguments
+ * @param callbacks the new interceptor(s) to use
+ * @return new instance of the same type
+ */
+ Object newInstance(Class[] types, Object[] args, Callback[] callbacks);
+
+ /**
+ * Return the <code>Callback</code> implementation at the specified index.
+ * @param index the callback index
+ * @return the callback implementation
+ */
+ Callback getCallback(int index);
+
+ /**
+ * Set the callback for this object for the given type.
+ * @param index the callback index to replace
+ * @param callback the new callback
+ */
+ void setCallback(int index, Callback callback);
+
+ /**
+ * Replace all of the callbacks for this object at once.
+ * @param callbacks the new callbacks(s) to use
+ */
+ void setCallbacks(Callback[] callbacks);
+
+ /**
+ * Get the current set of callbacks for ths object.
+ * @return a new array instance
+ */
+ Callback[] getCallbacks();
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/FixedValue.java b/cglib-and-asm/src/org/mockito/cglib/proxy/FixedValue.java
new file mode 100644
index 0000000..03cc0b3
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/FixedValue.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+/**
+ * {@link Enhancer} callback that simply returns the value to return
+ * from the proxied method. No information about what method
+ * is being called is available to the callback, and the type of
+ * the returned object must be compatible with the return type of
+ * the proxied method. This makes this callback primarily useful
+ * for forcing a particular method (through the use of a {@link CallbackFilter}
+ * to return a fixed value with little overhead.
+ */
+public interface FixedValue extends Callback {
+ /**
+ * Return the object which the original method invocation should
+ * return. This method is called for <b>every</b> method invocation.
+ * @return an object matching the type of the return value for every
+ * method this callback is mapped to
+ */
+ Object loadObject() throws Exception;
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/FixedValueGenerator.java b/cglib-and-asm/src/org/mockito/cglib/proxy/FixedValueGenerator.java
new file mode 100644
index 0000000..5542b0d
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/FixedValueGenerator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.util.*;
+
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+class FixedValueGenerator implements CallbackGenerator {
+ public static final FixedValueGenerator INSTANCE = new FixedValueGenerator();
+ private static final Type FIXED_VALUE =
+ TypeUtils.parseType("org.mockito.cglib.proxy.FixedValue");
+ private static final Signature LOAD_OBJECT =
+ TypeUtils.parseSignature("Object loadObject()");
+
+ public void generate(ClassEmitter ce, Context context, List methods) {
+ for (Iterator it = methods.iterator(); it.hasNext();) {
+ MethodInfo method = (MethodInfo)it.next();
+ CodeEmitter e = context.beginMethod(ce, method);
+ context.emitCallback(e, context.getIndex(method));
+ e.invoke_interface(FIXED_VALUE, LOAD_OBJECT);
+ e.unbox_or_zero(e.getReturnType());
+ e.return_value();
+ e.end_method();
+ }
+ }
+
+ public void generateStatic(CodeEmitter e, Context context, List methods) { }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/InterfaceMaker.java b/cglib-and-asm/src/org/mockito/cglib/proxy/InterfaceMaker.java
new file mode 100644
index 0000000..67627fe
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/InterfaceMaker.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+/**
+ * Generates new interfaces at runtime.
+ * By passing a generated interface to the Enhancer's list of interfaces to
+ * implement, you can make your enhanced classes handle an arbitrary set
+ * of method signatures.
+ * @author Chris Nokleberg
+ * @version $Id: InterfaceMaker.java,v 1.4 2006/03/05 02:43:19 herbyderby Exp $
+ */
+public class InterfaceMaker extends AbstractClassGenerator
+{
+ private static final Source SOURCE = new Source(InterfaceMaker.class.getName());
+ private Map signatures = new HashMap();
+
+ /**
+ * Create a new <code>InterfaceMaker</code>. A new <code>InterfaceMaker</code>
+ * object should be used for each generated interface, and should not
+ * be shared across threads.
+ */
+ public InterfaceMaker() {
+ super(SOURCE);
+ }
+
+ /**
+ * Add a method signature to the interface.
+ * @param sig the method signature to add to the interface
+ * @param exceptions an array of exception types to declare for the method
+ */
+ public void add(Signature sig, Type[] exceptions) {
+ signatures.put(sig, exceptions);
+ }
+
+ /**
+ * Add a method signature to the interface. The method modifiers are ignored,
+ * since interface methods are by definition abstract and public.
+ * @param method the method to add to the interface
+ */
+ public void add(Method method) {
+ add(ReflectUtils.getSignature(method),
+ ReflectUtils.getExceptionTypes(method));
+ }
+
+ /**
+ * Add all the public methods in the specified class.
+ * Methods from superclasses are included, except for methods declared in the base
+ * Object class (e.g. <code>getClass</code>, <code>equals</code>, <code>hashCode</code>).
+ * @param class the class containing the methods to add to the interface
+ */
+ public void add(Class clazz) {
+ Method[] methods = clazz.getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ Method m = methods[i];
+ if (!m.getDeclaringClass().getName().equals("java.lang.Object")) {
+ add(m);
+ }
+ }
+ }
+
+ /**
+ * Create an interface using the current set of method signatures.
+ */
+ public Class create() {
+ setUseCache(false);
+ return (Class)super.create(this);
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return null;
+ }
+
+ protected Object firstInstance(Class type) {
+ return type;
+ }
+
+ protected Object nextInstance(Object instance) {
+ throw new IllegalStateException("InterfaceMaker does not cache");
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ ClassEmitter ce = new ClassEmitter(v);
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC | Constants.ACC_INTERFACE,
+ getClassName(),
+ null,
+ null,
+ Constants.SOURCE_FILE);
+ for (Iterator it = signatures.keySet().iterator(); it.hasNext();) {
+ Signature sig = (Signature)it.next();
+ Type[] exceptions = (Type[])signatures.get(sig);
+ ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT,
+ sig,
+ exceptions).end_method();
+ }
+ ce.end_class();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/InvocationHandler.java b/cglib-and-asm/src/org/mockito/cglib/proxy/InvocationHandler.java
new file mode 100644
index 0000000..86314bb
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/InvocationHandler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.lang.reflect.Method;
+
+/**
+ * {@link java.lang.reflect.InvocationHandler} replacement (unavailable under JDK 1.2).
+ * This callback type is primarily for use by the {@link Proxy} class but
+ * may be used with {@link Enhancer} as well.
+ * @author Neeme Praks <a href="mailto:neeme@apache.org">neeme@apache.org</a>
+ * @version $Id: InvocationHandler.java,v 1.3 2004/06/24 21:15:20 herbyderby Exp $
+ */
+public interface InvocationHandler
+extends Callback
+{
+ /**
+ * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object)
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
+
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/InvocationHandlerGenerator.java b/cglib-and-asm/src/org/mockito/cglib/proxy/InvocationHandlerGenerator.java
new file mode 100644
index 0000000..bb40631
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/InvocationHandlerGenerator.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.util.*;
+
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+class InvocationHandlerGenerator
+implements CallbackGenerator
+{
+ public static final InvocationHandlerGenerator INSTANCE = new InvocationHandlerGenerator();
+
+ private static final Type INVOCATION_HANDLER =
+ TypeUtils.parseType("org.mockito.cglib.proxy.InvocationHandler");
+ private static final Type UNDECLARED_THROWABLE_EXCEPTION =
+ TypeUtils.parseType("org.mockito.cglib.proxy.UndeclaredThrowableException");
+ private static final Type METHOD =
+ TypeUtils.parseType("java.lang.reflect.Method");
+ private static final Signature INVOKE =
+ TypeUtils.parseSignature("Object invoke(Object, java.lang.reflect.Method, Object[])");
+
+ public void generate(ClassEmitter ce, Context context, List methods) {
+ for (Iterator it = methods.iterator(); it.hasNext();) {
+ MethodInfo method = (MethodInfo)it.next();
+ Signature impl = context.getImplSignature(method);
+ ce.declare_field(Constants.PRIVATE_FINAL_STATIC, impl.getName(), METHOD, null);
+
+ CodeEmitter e = context.beginMethod(ce, method);
+ Block handler = e.begin_block();
+ context.emitCallback(e, context.getIndex(method));
+ e.load_this();
+ e.getfield(impl.getName());
+ e.create_arg_array();
+ e.invoke_interface(INVOCATION_HANDLER, INVOKE);
+ e.unbox(method.getSignature().getReturnType());
+ e.return_value();
+ handler.end();
+ EmitUtils.wrap_undeclared_throwable(e, handler, method.getExceptionTypes(), UNDECLARED_THROWABLE_EXCEPTION);
+ e.end_method();
+ }
+ }
+
+ public void generateStatic(CodeEmitter e, Context context, List methods) {
+ for (Iterator it = methods.iterator(); it.hasNext();) {
+ MethodInfo method = (MethodInfo)it.next();
+ EmitUtils.load_method(e, method);
+ e.putfield(context.getImplSignature(method).getName());
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/LazyLoader.java b/cglib-and-asm/src/org/mockito/cglib/proxy/LazyLoader.java
new file mode 100644
index 0000000..35cc261
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/LazyLoader.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+/**
+ * Lazy-loading {@link Enhancer} callback.
+ */
+public interface LazyLoader extends Callback {
+ /**
+ * Return the object which the original method invocation should be
+ * dispatched. Called as soon as the first lazily-loaded method in
+ * the enhanced instance is invoked. The same object is then used
+ * for every future method call to the proxy instance.
+ * @return an object that can invoke the method
+ */
+ Object loadObject() throws Exception;
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/LazyLoaderGenerator.java b/cglib-and-asm/src/org/mockito/cglib/proxy/LazyLoaderGenerator.java
new file mode 100644
index 0000000..a5b68b0
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/LazyLoaderGenerator.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.util.*;
+
+import org.mockito.asm.Label;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+class LazyLoaderGenerator implements CallbackGenerator {
+ public static final LazyLoaderGenerator INSTANCE = new LazyLoaderGenerator();
+
+ private static final Signature LOAD_OBJECT =
+ TypeUtils.parseSignature("Object loadObject()");
+ private static final Type LAZY_LOADER =
+ TypeUtils.parseType("org.mockito.cglib.proxy.LazyLoader");
+
+ public void generate(ClassEmitter ce, Context context, List methods) {
+ Set indexes = new HashSet();
+ for (Iterator it = methods.iterator(); it.hasNext();) {
+ MethodInfo method = (MethodInfo)it.next();
+ if (TypeUtils.isProtected(method.getModifiers())) {
+ // ignore protected methods
+ } else {
+ int index = context.getIndex(method);
+ indexes.add(new Integer(index));
+ CodeEmitter e = context.beginMethod(ce, method);
+ e.load_this();
+ e.dup();
+ e.invoke_virtual_this(loadMethod(index));
+ e.checkcast(method.getClassInfo().getType());
+ e.load_args();
+ e.invoke(method);
+ e.return_value();
+ e.end_method();
+ }
+ }
+
+ for (Iterator it = indexes.iterator(); it.hasNext();) {
+ int index = ((Integer)it.next()).intValue();
+
+ String delegate = "CGLIB$LAZY_LOADER_" + index;
+ ce.declare_field(Constants.ACC_PRIVATE, delegate, Constants.TYPE_OBJECT, null);
+
+ CodeEmitter e = ce.begin_method(Constants.ACC_PRIVATE |
+ Constants.ACC_SYNCHRONIZED |
+ Constants.ACC_FINAL,
+ loadMethod(index),
+ null);
+ e.load_this();
+ e.getfield(delegate);
+ e.dup();
+ Label end = e.make_label();
+ e.ifnonnull(end);
+ e.pop();
+ e.load_this();
+ context.emitCallback(e, index);
+ e.invoke_interface(LAZY_LOADER, LOAD_OBJECT);
+ e.dup_x1();
+ e.putfield(delegate);
+ e.mark(end);
+ e.return_value();
+ e.end_method();
+
+ }
+ }
+
+ private Signature loadMethod(int index) {
+ return new Signature("CGLIB$LOAD_PRIVATE_" + index,
+ Constants.TYPE_OBJECT,
+ Constants.TYPES_EMPTY);
+ }
+
+ public void generateStatic(CodeEmitter e, Context context, List methods) { }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/MethodInterceptor.java b/cglib-and-asm/src/org/mockito/cglib/proxy/MethodInterceptor.java
new file mode 100644
index 0000000..749dcf2
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/MethodInterceptor.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002,2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+/**
+ * General-purpose {@link Enhancer} callback which provides for "around advice".
+ * @author Juozas Baliuka <a href="mailto:baliuka@mwm.lt">baliuka@mwm.lt</a>
+ * @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $
+ */
+public interface MethodInterceptor
+extends Callback
+{
+ /**
+ * All generated proxied methods call this method instead of the original method.
+ * The original method may either be invoked by normal reflection using the Method object,
+ * or by using the MethodProxy (faster).
+ * @param obj "this", the enhanced object
+ * @param method intercepted Method
+ * @param args argument array; primitive types are wrapped
+ * @param proxy used to invoke super (non-intercepted method); may be called
+ * as many times as needed
+ * @throws Throwable any exception may be thrown; if so, super method will not be invoked
+ * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
+ * @see MethodProxy
+ */
+ public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
+ MethodProxy proxy) throws Throwable;
+
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/MethodInterceptorGenerator.java b/cglib-and-asm/src/org/mockito/cglib/proxy/MethodInterceptorGenerator.java
new file mode 100644
index 0000000..12e33de
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/MethodInterceptorGenerator.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+import org.mockito.asm.Label;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+class MethodInterceptorGenerator
+implements CallbackGenerator
+{
+ public static final MethodInterceptorGenerator INSTANCE = new MethodInterceptorGenerator();
+
+ static final String EMPTY_ARGS_NAME = "CGLIB$emptyArgs";
+ static final String FIND_PROXY_NAME = "CGLIB$findMethodProxy";
+ static final Class[] FIND_PROXY_TYPES = { Signature.class };
+
+ private static final Type ABSTRACT_METHOD_ERROR =
+ TypeUtils.parseType("AbstractMethodError");
+ private static final Type METHOD =
+ TypeUtils.parseType("java.lang.reflect.Method");
+ private static final Type REFLECT_UTILS =
+ TypeUtils.parseType("org.mockito.cglib.core.ReflectUtils");
+ private static final Type METHOD_PROXY =
+ TypeUtils.parseType("org.mockito.cglib.proxy.MethodProxy");
+ private static final Type METHOD_INTERCEPTOR =
+ TypeUtils.parseType("org.mockito.cglib.proxy.MethodInterceptor");
+ private static final Signature GET_DECLARED_METHODS =
+ TypeUtils.parseSignature("java.lang.reflect.Method[] getDeclaredMethods()");
+ private static final Signature GET_DECLARING_CLASS =
+ TypeUtils.parseSignature("Class getDeclaringClass()");
+ private static final Signature FIND_METHODS =
+ TypeUtils.parseSignature("java.lang.reflect.Method[] findMethods(String[], java.lang.reflect.Method[])");
+ private static final Signature MAKE_PROXY =
+ new Signature("create", METHOD_PROXY, new Type[]{
+ Constants.TYPE_CLASS,
+ Constants.TYPE_CLASS,
+ Constants.TYPE_STRING,
+ Constants.TYPE_STRING,
+ Constants.TYPE_STRING
+ });
+ private static final Signature INTERCEPT =
+ new Signature("intercept", Constants.TYPE_OBJECT, new Type[]{
+ Constants.TYPE_OBJECT,
+ METHOD,
+ Constants.TYPE_OBJECT_ARRAY,
+ METHOD_PROXY
+ });
+ private static final Signature FIND_PROXY =
+ new Signature(FIND_PROXY_NAME, METHOD_PROXY, new Type[]{ Constants.TYPE_SIGNATURE });
+ private static final Signature TO_STRING =
+ TypeUtils.parseSignature("String toString()");
+ private static final Transformer METHOD_TO_CLASS = new Transformer(){
+ public Object transform(Object value) {
+ return ((MethodInfo)value).getClassInfo();
+ }
+ };
+ private static final Signature CSTRUCT_SIGNATURE =
+ TypeUtils.parseConstructor("String, String");
+
+ private String getMethodField(Signature impl) {
+ return impl.getName() + "$Method";
+ }
+ private String getMethodProxyField(Signature impl) {
+ return impl.getName() + "$Proxy";
+ }
+
+ public void generate(ClassEmitter ce, Context context, List methods) {
+ Map sigMap = new HashMap();
+ for (Iterator it = methods.iterator(); it.hasNext();) {
+ MethodInfo method = (MethodInfo)it.next();
+ Signature sig = method.getSignature();
+ Signature impl = context.getImplSignature(method);
+
+ String methodField = getMethodField(impl);
+ String methodProxyField = getMethodProxyField(impl);
+
+ sigMap.put(sig.toString(), methodProxyField);
+ ce.declare_field(Constants.PRIVATE_FINAL_STATIC, methodField, METHOD, null);
+ ce.declare_field(Constants.PRIVATE_FINAL_STATIC, methodProxyField, METHOD_PROXY, null);
+ ce.declare_field(Constants.PRIVATE_FINAL_STATIC, EMPTY_ARGS_NAME, Constants.TYPE_OBJECT_ARRAY, null);
+ CodeEmitter e;
+
+ // access method
+ e = ce.begin_method(Constants.ACC_FINAL,
+ impl,
+ method.getExceptionTypes());
+ superHelper(e, method);
+ e.return_value();
+ e.end_method();
+
+ // around method
+ e = context.beginMethod(ce, method);
+ Label nullInterceptor = e.make_label();
+ context.emitCallback(e, context.getIndex(method));
+ e.dup();
+ e.ifnull(nullInterceptor);
+
+ e.load_this();
+ e.getfield(methodField);
+
+ if (sig.getArgumentTypes().length == 0) {
+ e.getfield(EMPTY_ARGS_NAME);
+ } else {
+ e.create_arg_array();
+ }
+
+ e.getfield(methodProxyField);
+ e.invoke_interface(METHOD_INTERCEPTOR, INTERCEPT);
+ e.unbox_or_zero(sig.getReturnType());
+ e.return_value();
+
+ e.mark(nullInterceptor);
+ superHelper(e, method);
+ e.return_value();
+ e.end_method();
+ }
+ generateFindProxy(ce, sigMap);
+ }
+
+ private static void superHelper(CodeEmitter e, MethodInfo method)
+ {
+ if (TypeUtils.isAbstract(method.getModifiers())) {
+ e.throw_exception(ABSTRACT_METHOD_ERROR, method.toString() + " is abstract" );
+ } else {
+ e.load_this();
+ e.load_args();
+ e.super_invoke(method.getSignature());
+ }
+ }
+
+ public void generateStatic(CodeEmitter e, Context context, List methods) throws Exception {
+ /* generates:
+ static {
+ Class thisClass = Class.forName("NameOfThisClass");
+ Class cls = Class.forName("java.lang.Object");
+ String[] sigs = new String[]{ "toString", "()Ljava/lang/String;", ... };
+ Method[] methods = cls.getDeclaredMethods();
+ methods = ReflectUtils.findMethods(sigs, methods);
+ METHOD_0 = methods[0];
+ CGLIB$ACCESS_0 = MethodProxy.create(cls, thisClass, "()Ljava/lang/String;", "toString", "CGLIB$ACCESS_0");
+ ...
+ }
+ */
+
+ e.push(0);
+ e.newarray();
+ e.putfield(EMPTY_ARGS_NAME);
+
+ Local thisclass = e.make_local();
+ Local declaringclass = e.make_local();
+ EmitUtils.load_class_this(e);
+ e.store_local(thisclass);
+
+ Map methodsByClass = CollectionUtils.bucket(methods, METHOD_TO_CLASS);
+ for (Iterator i = methodsByClass.keySet().iterator(); i.hasNext();) {
+ ClassInfo classInfo = (ClassInfo)i.next();
+
+ List classMethods = (List)methodsByClass.get(classInfo);
+ e.push(2 * classMethods.size());
+ e.newarray(Constants.TYPE_STRING);
+ for (int index = 0; index < classMethods.size(); index++) {
+ MethodInfo method = (MethodInfo)classMethods.get(index);
+ Signature sig = method.getSignature();
+ e.dup();
+ e.push(2 * index);
+ e.push(sig.getName());
+ e.aastore();
+ e.dup();
+ e.push(2 * index + 1);
+ e.push(sig.getDescriptor());
+ e.aastore();
+ }
+
+ EmitUtils.load_class(e, classInfo.getType());
+ e.dup();
+ e.store_local(declaringclass);
+ e.invoke_virtual(Constants.TYPE_CLASS, GET_DECLARED_METHODS);
+ e.invoke_static(REFLECT_UTILS, FIND_METHODS);
+
+ for (int index = 0; index < classMethods.size(); index++) {
+ MethodInfo method = (MethodInfo)classMethods.get(index);
+ Signature sig = method.getSignature();
+ Signature impl = context.getImplSignature(method);
+ e.dup();
+ e.push(index);
+ e.array_load(METHOD);
+ e.putfield(getMethodField(impl));
+
+ e.load_local(declaringclass);
+ e.load_local(thisclass);
+ e.push(sig.getDescriptor());
+ e.push(sig.getName());
+ e.push(impl.getName());
+ e.invoke_static(METHOD_PROXY, MAKE_PROXY);
+ e.putfield(getMethodProxyField(impl));
+ }
+ e.pop();
+ }
+ }
+
+ public void generateFindProxy(ClassEmitter ce, final Map sigMap) {
+ final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
+ FIND_PROXY,
+ null);
+ e.load_arg(0);
+ e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING);
+ ObjectSwitchCallback callback = new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ e.getfield((String)sigMap.get(key));
+ e.return_value();
+ }
+ public void processDefault() {
+ e.aconst_null();
+ e.return_value();
+ }
+ };
+ EmitUtils.string_switch(e,
+ (String[])sigMap.keySet().toArray(new String[0]),
+ Constants.SWITCH_STYLE_HASH,
+ callback);
+ e.end_method();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/MethodProxy.java b/cglib-and-asm/src/org/mockito/cglib/proxy/MethodProxy.java
new file mode 100644
index 0000000..2e9a65f
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/MethodProxy.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.mockito.cglib.core.AbstractClassGenerator;
+import org.mockito.cglib.core.CodeGenerationException;
+import org.mockito.cglib.core.GeneratorStrategy;
+import org.mockito.cglib.core.NamingPolicy;
+import org.mockito.cglib.core.Signature;
+import org.mockito.cglib.reflect.FastClass;
+
+
+/**
+ * Classes generated by {@link Enhancer} pass this object to the
+ * registered {@link MethodInterceptor} objects when an intercepted method is invoked. It can
+ * be used to either invoke the original method, or call the same method on a different
+ * object of the same type.
+ * @version $Id: MethodProxy.java,v 1.14 2008/05/26 04:05:50 herbyderby Exp $
+ */
+public class MethodProxy {
+ private Signature sig1;
+ private Signature sig2;
+ private CreateInfo createInfo;
+
+ private final Object initLock = new Object();
+ private volatile FastClassInfo fastClassInfo;
+
+ /**
+ * For internal use by {@link Enhancer} only; see the {@link org.mockito.cglib.reflect.FastMethod} class
+ * for similar functionality.
+ */
+ public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
+ MethodProxy proxy = new MethodProxy();
+ proxy.sig1 = new Signature(name1, desc);
+ proxy.sig2 = new Signature(name2, desc);
+ proxy.createInfo = new CreateInfo(c1, c2);
+ return proxy;
+ }
+
+ private void init()
+ {
+ /*
+ * Using a volatile invariant allows us to initialize the FastClass and
+ * method index pairs atomically.
+ *
+ * Double-checked locking is safe with volatile in Java 5. Before 1.5 this
+ * code could allow fastClassInfo to be instantiated more than once, which
+ * appears to be benign.
+ */
+ if (fastClassInfo == null)
+ {
+ synchronized (initLock)
+ {
+ if (fastClassInfo == null)
+ {
+ CreateInfo ci = createInfo;
+
+ FastClassInfo fci = new FastClassInfo();
+ fci.f1 = helper(ci, ci.c1);
+ fci.f2 = helper(ci, ci.c2);
+ fci.i1 = fci.f1.getIndex(sig1);
+ fci.i2 = fci.f2.getIndex(sig2);
+ fastClassInfo = fci;
+ }
+ }
+ }
+ }
+
+ private static class FastClassInfo
+ {
+ FastClass f1;
+ FastClass f2;
+ int i1;
+ int i2;
+ }
+
+ private static class CreateInfo
+ {
+ Class c1;
+ Class c2;
+ NamingPolicy namingPolicy;
+ GeneratorStrategy strategy;
+ boolean attemptLoad;
+
+ public CreateInfo(Class c1, Class c2)
+ {
+ this.c1 = c1;
+ this.c2 = c2;
+ AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
+ if (fromEnhancer != null) {
+ namingPolicy = fromEnhancer.getNamingPolicy();
+ strategy = fromEnhancer.getStrategy();
+ attemptLoad = fromEnhancer.getAttemptLoad();
+ }
+ }
+ }
+
+ private static FastClass helper(CreateInfo ci, Class type) {
+ FastClass.Generator g = new FastClass.Generator();
+ g.setType(type);
+ g.setClassLoader(ci.c2.getClassLoader());
+ g.setNamingPolicy(ci.namingPolicy);
+ g.setStrategy(ci.strategy);
+ g.setAttemptLoad(ci.attemptLoad);
+ return g.create();
+ }
+
+ private MethodProxy() {
+ }
+
+ /**
+ * Return the signature of the proxied method.
+ */
+ public Signature getSignature() {
+ return sig1;
+ }
+
+ /**
+ * Return the name of the synthetic method created by CGLIB which is
+ * used by {@link #invokeSuper} to invoke the superclass
+ * (non-intercepted) method implementation. The parameter types are
+ * the same as the proxied method.
+ */
+ public String getSuperName() {
+ return sig2.getName();
+ }
+
+ /**
+ * Return the {@link org.mockito.cglib.reflect.FastClass} method index
+ * for the method used by {@link #invokeSuper}. This index uniquely
+ * identifies the method within the generated proxy, and therefore
+ * can be useful to reference external metadata.
+ * @see #getSuperName
+ */
+ public int getSuperIndex() {
+ init();
+ return fastClassInfo.i2;
+ }
+
+ /**
+ * Return the <code>MethodProxy</code> used when intercepting the method
+ * matching the given signature.
+ * @param type the class generated by Enhancer
+ * @param sig the signature to match
+ * @return the MethodProxy instance, or null if no applicable matching method is found
+ * @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor
+ */
+ public static MethodProxy find(Class type, Signature sig) {
+ try {
+ Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME,
+ MethodInterceptorGenerator.FIND_PROXY_TYPES);
+ return (MethodProxy)m.invoke(null, new Object[]{ sig });
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");
+ } catch (IllegalAccessException e) {
+ throw new CodeGenerationException(e);
+ } catch (InvocationTargetException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ /**
+ * Invoke the original method, on a different object of the same type.
+ * @param obj the compatible object; recursion will result if you use the object passed as the first
+ * argument to the MethodInterceptor (usually not what you want)
+ * @param args the arguments passed to the intercepted method; you may substitute a different
+ * argument array as long as the types are compatible
+ * @see MethodInterceptor#intercept
+ * @throws Throwable the bare exceptions thrown by the called method are passed through
+ * without wrapping in an <code>InvocationTargetException</code>
+ */
+ public Object invoke(Object obj, Object[] args) throws Throwable {
+ try {
+ init();
+ FastClassInfo fci = fastClassInfo;
+ return fci.f1.invoke(fci.i1, obj, args);
+ } catch (InvocationTargetException e) {
+ throw e.getTargetException();
+ } catch (IllegalArgumentException e) {
+ if (fastClassInfo.i1 < 0)
+ throw new IllegalArgumentException("Protected method: " + sig1);
+ throw e;
+ }
+ }
+
+ /**
+ * Invoke the original (super) method on the specified object.
+ * @param obj the enhanced object, must be the object passed as the first
+ * argument to the MethodInterceptor
+ * @param args the arguments passed to the intercepted method; you may substitute a different
+ * argument array as long as the types are compatible
+ * @see MethodInterceptor#intercept
+ * @throws Throwable the bare exceptions thrown by the called method are passed through
+ * without wrapping in an <code>InvocationTargetException</code>
+ */
+ public Object invokeSuper(Object obj, Object[] args) throws Throwable {
+ try {
+ init();
+ FastClassInfo fci = fastClassInfo;
+ return fci.f2.invoke(fci.i2, obj, args);
+ } catch (InvocationTargetException e) {
+ throw e.getTargetException();
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/Mixin.java b/cglib-and-asm/src/org/mockito/cglib/proxy/Mixin.java
new file mode 100644
index 0000000..b189ba7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/Mixin.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.cglib.core.*;
+
+
+
+/**
+ * <code>Mixin</code> allows
+ * multiple objects to be combined into a single larger object. The
+ * methods in the generated object simply call the original methods in the
+ * underlying "delegate" objects.
+ * @author Chris Nokleberg
+ * @version $Id: Mixin.java,v 1.7 2005/09/27 11:42:27 baliuka Exp $
+ */
+abstract public class Mixin {
+ private static final MixinKey KEY_FACTORY =
+ (MixinKey)KeyFactory.create(MixinKey.class, KeyFactory.CLASS_BY_NAME);
+ private static final Map ROUTE_CACHE = Collections.synchronizedMap(new HashMap());
+
+ public static final int STYLE_INTERFACES = 0;
+ public static final int STYLE_BEANS = 1;
+ public static final int STYLE_EVERYTHING = 2;
+
+ interface MixinKey {
+ public Object newInstance(int style, String[] classes, int[] route);
+ }
+
+ abstract public Mixin newInstance(Object[] delegates);
+
+ /**
+ * Helper method to create an interface mixin. For finer control over the
+ * generated instance, use a new instance of <code>Mixin</code>
+ * instead of this static method.
+ * TODO
+ */
+ public static Mixin create(Object[] delegates) {
+ Generator gen = new Generator();
+ gen.setDelegates(delegates);
+ return gen.create();
+ }
+
+ /**
+ * Helper method to create an interface mixin. For finer control over the
+ * generated instance, use a new instance of <code>Mixin</code>
+ * instead of this static method.
+ * TODO
+ */
+ public static Mixin create(Class[] interfaces, Object[] delegates) {
+ Generator gen = new Generator();
+ gen.setClasses(interfaces);
+ gen.setDelegates(delegates);
+ return gen.create();
+ }
+
+
+ public static Mixin createBean(Object[] beans) {
+
+ return createBean(null, beans);
+
+ }
+ /**
+ * Helper method to create a bean mixin. For finer control over the
+ * generated instance, use a new instance of <code>Mixin</code>
+ * instead of this static method.
+ * TODO
+ */
+ public static Mixin createBean(ClassLoader loader,Object[] beans) {
+ Generator gen = new Generator();
+ gen.setStyle(STYLE_BEANS);
+ gen.setDelegates(beans);
+ gen.setClassLoader(loader);
+ return gen.create();
+ }
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(Mixin.class.getName());
+
+ private Class[] classes;
+ private Object[] delegates;
+ private int style = STYLE_INTERFACES;
+
+ private int[] route;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return classes[0].getClassLoader(); // is this right?
+ }
+
+ public void setStyle(int style) {
+ switch (style) {
+ case STYLE_INTERFACES:
+ case STYLE_BEANS:
+ case STYLE_EVERYTHING:
+ this.style = style;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown mixin style: " + style);
+ }
+ }
+
+ public void setClasses(Class[] classes) {
+ this.classes = classes;
+ }
+
+ public void setDelegates(Object[] delegates) {
+ this.delegates = delegates;
+ }
+
+ public Mixin create() {
+ if (classes == null && delegates == null) {
+ throw new IllegalStateException("Either classes or delegates must be set");
+ }
+ switch (style) {
+ case STYLE_INTERFACES:
+ if (classes == null) {
+ Route r = route(delegates);
+ classes = r.classes;
+ route = r.route;
+ }
+ break;
+ case STYLE_BEANS:
+ // fall-through
+ case STYLE_EVERYTHING:
+ if (classes == null) {
+ classes = ReflectUtils.getClasses(delegates);
+ } else {
+ if (delegates != null) {
+ Class[] temp = ReflectUtils.getClasses(delegates);
+ if (classes.length != temp.length) {
+ throw new IllegalStateException("Specified classes are incompatible with delegates");
+ }
+ for (int i = 0; i < classes.length; i++) {
+ if (!classes[i].isAssignableFrom(temp[i])) {
+ throw new IllegalStateException("Specified class " + classes[i] + " is incompatible with delegate class " + temp[i] + " (index " + i + ")");
+ }
+ }
+ }
+ }
+ }
+ setNamePrefix(classes[ReflectUtils.findPackageProtected(classes)].getName());
+
+ return (Mixin)super.create(KEY_FACTORY.newInstance(style, ReflectUtils.getNames( classes ), route));
+ }
+
+ public void generateClass(ClassVisitor v) {
+ switch (style) {
+ case STYLE_INTERFACES:
+ new MixinEmitter(v, getClassName(), classes, route);
+ break;
+ case STYLE_BEANS:
+ new MixinBeanEmitter(v, getClassName(), classes);
+ break;
+ case STYLE_EVERYTHING:
+ new MixinEverythingEmitter(v, getClassName(), classes);
+ break;
+ }
+ }
+
+ protected Object firstInstance(Class type) {
+ return ((Mixin)ReflectUtils.newInstance(type)).newInstance(delegates);
+ }
+
+ protected Object nextInstance(Object instance) {
+ return ((Mixin)instance).newInstance(delegates);
+ }
+ }
+
+ public static Class[] getClasses(Object[] delegates) {
+ return (Class[])route(delegates).classes.clone();
+ }
+
+// public static int[] getRoute(Object[] delegates) {
+// return (int[])route(delegates).route.clone();
+// }
+
+ private static Route route(Object[] delegates) {
+ Object key = ClassesKey.create(delegates);
+ Route route = (Route)ROUTE_CACHE.get(key);
+ if (route == null) {
+ ROUTE_CACHE.put(key, route = new Route(delegates));
+ }
+ return route;
+ }
+
+ private static class Route
+ {
+ private Class[] classes;
+ private int[] route;
+
+ Route(Object[] delegates) {
+ Map map = new HashMap();
+ ArrayList collect = new ArrayList();
+ for (int i = 0; i < delegates.length; i++) {
+ Class delegate = delegates[i].getClass();
+ collect.clear();
+ ReflectUtils.addAllInterfaces(delegate, collect);
+ for (Iterator it = collect.iterator(); it.hasNext();) {
+ Class iface = (Class)it.next();
+ if (!map.containsKey(iface)) {
+ map.put(iface, new Integer(i));
+ }
+ }
+ }
+ classes = new Class[map.size()];
+ route = new int[map.size()];
+ int index = 0;
+ for (Iterator it = map.keySet().iterator(); it.hasNext();) {
+ Class key = (Class)it.next();
+ classes[index] = key;
+ route[index] = ((Integer)map.get(key)).intValue();
+ index++;
+ }
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/MixinBeanEmitter.java b/cglib-and-asm/src/org/mockito/cglib/proxy/MixinBeanEmitter.java
new file mode 100644
index 0000000..9b47798
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/MixinBeanEmitter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.lang.reflect.Method;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.cglib.core.ReflectUtils;
+
+/**
+ * @author Chris Nokleberg
+ * @version $Id: MixinBeanEmitter.java,v 1.2 2004/06/24 21:15:20 herbyderby Exp $
+ */
+class MixinBeanEmitter extends MixinEmitter {
+ public MixinBeanEmitter(ClassVisitor v, String className, Class[] classes) {
+ super(v, className, classes, null);
+ }
+
+ protected Class[] getInterfaces(Class[] classes) {
+ return null;
+ }
+
+ protected Method[] getMethods(Class type) {
+ return ReflectUtils.getPropertyMethods(ReflectUtils.getBeanProperties(type), true, true);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/MixinEmitter.java b/cglib-and-asm/src/org/mockito/cglib/proxy/MixinEmitter.java
new file mode 100644
index 0000000..f4460b4
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/MixinEmitter.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+/**
+ * @author Chris Nokleberg
+ * @version $Id: MixinEmitter.java,v 1.9 2006/08/27 21:04:37 herbyderby Exp $
+ */
+class MixinEmitter extends ClassEmitter {
+ private static final String FIELD_NAME = "CGLIB$DELEGATES";
+ private static final Signature CSTRUCT_OBJECT_ARRAY =
+ TypeUtils.parseConstructor("Object[]");
+ private static final Type MIXIN =
+ TypeUtils.parseType("org.mockito.cglib.proxy.Mixin");
+ private static final Signature NEW_INSTANCE =
+ new Signature("newInstance", MIXIN, new Type[]{ Constants.TYPE_OBJECT_ARRAY });
+
+ public MixinEmitter(ClassVisitor v, String className, Class[] classes, int[] route) {
+ super(v);
+
+ begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ className,
+ MIXIN,
+ TypeUtils.getTypes(getInterfaces(classes)),
+ Constants.SOURCE_FILE);
+ EmitUtils.null_constructor(this);
+ EmitUtils.factory_method(this, NEW_INSTANCE);
+
+ declare_field(Constants.ACC_PRIVATE, FIELD_NAME, Constants.TYPE_OBJECT_ARRAY, null);
+
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT_ARRAY, null);
+ e.load_this();
+ e.super_invoke_constructor();
+ e.load_this();
+ e.load_arg(0);
+ e.putfield(FIELD_NAME);
+ e.return_value();
+ e.end_method();
+
+ Set unique = new HashSet();
+ for (int i = 0; i < classes.length; i++) {
+ Method[] methods = getMethods(classes[i]);
+ for (int j = 0; j < methods.length; j++) {
+ if (unique.add(MethodWrapper.create(methods[j]))) {
+ MethodInfo method = ReflectUtils.getMethodInfo(methods[j]);
+ e = EmitUtils.begin_method(this, method, Constants.ACC_PUBLIC);
+ e.load_this();
+ e.getfield(FIELD_NAME);
+ e.aaload((route != null) ? route[i] : i);
+ e.checkcast(method.getClassInfo().getType());
+ e.load_args();
+ e.invoke(method);
+ e.return_value();
+ e.end_method();
+ }
+ }
+ }
+
+ end_class();
+ }
+
+ protected Class[] getInterfaces(Class[] classes) {
+ return classes;
+ }
+
+ protected Method[] getMethods(Class type) {
+ return type.getMethods();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/MixinEverythingEmitter.java b/cglib-and-asm/src/org/mockito/cglib/proxy/MixinEverythingEmitter.java
new file mode 100644
index 0000000..2586032
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/MixinEverythingEmitter.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.cglib.core.CollectionUtils;
+import org.mockito.cglib.core.ReflectUtils;
+import org.mockito.cglib.core.RejectModifierPredicate;
+
+/**
+ * @author Chris Nokleberg
+ * @version $Id: MixinEverythingEmitter.java,v 1.3 2004/06/24 21:15:19 herbyderby Exp $
+ */
+class MixinEverythingEmitter extends MixinEmitter {
+
+ public MixinEverythingEmitter(ClassVisitor v, String className, Class[] classes) {
+ super(v, className, classes, null);
+ }
+
+ protected Class[] getInterfaces(Class[] classes) {
+ List list = new ArrayList();
+ for (int i = 0; i < classes.length; i++) {
+ ReflectUtils.addAllInterfaces(classes[i], list);
+ }
+ return (Class[])list.toArray(new Class[list.size()]);
+ }
+
+ protected Method[] getMethods(Class type) {
+ List methods = new ArrayList(Arrays.asList(type.getMethods()));
+ CollectionUtils.filter(methods, new RejectModifierPredicate(Modifier.FINAL | Modifier.STATIC));
+ return (Method[])methods.toArray(new Method[methods.size()]);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/NoOp.java b/cglib-and-asm/src/org/mockito/cglib/proxy/NoOp.java
new file mode 100644
index 0000000..727573f
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/NoOp.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+/**
+ * Methods using this {@link Enhancer} callback will delegate directly to the
+ * default (super) implementation in the base class.
+ */
+public interface NoOp extends Callback
+{
+ /**
+ * A thread-safe singleton instance of the <code>NoOp</code> callback.
+ */
+ public static final NoOp INSTANCE = new NoOp() { };
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/NoOpGenerator.java b/cglib-and-asm/src/org/mockito/cglib/proxy/NoOpGenerator.java
new file mode 100644
index 0000000..16701a7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/NoOpGenerator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.mockito.cglib.core.*;
+
+class NoOpGenerator
+implements CallbackGenerator
+{
+ public static final NoOpGenerator INSTANCE = new NoOpGenerator();
+
+ public void generate(ClassEmitter ce, Context context, List methods) {
+ for (Iterator it = methods.iterator(); it.hasNext();) {
+ MethodInfo method = (MethodInfo)it.next();
+ if (TypeUtils.isProtected(context.getOriginalModifiers(method)) &&
+ TypeUtils.isPublic(method.getModifiers())) {
+ CodeEmitter e = EmitUtils.begin_method(ce, method);
+ e.load_this();
+ e.load_args();
+ e.super_invoke();
+ e.return_value();
+ e.end_method();
+ }
+ }
+ }
+
+ public void generateStatic(CodeEmitter e, Context context, List methods) { }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/Proxy.java b/cglib-and-asm/src/org/mockito/cglib/proxy/Proxy.java
new file mode 100644
index 0000000..80387e0
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/Proxy.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.mockito.cglib.core.CodeGenerationException;
+
+/**
+ * This class is meant to be used as replacement for
+ * <code>java.lang.reflect.Proxy</code> under JDK 1.2. There are some known
+ * subtle differences:
+ * <ul>
+ * <li>The exceptions returned by invoking <code>getExceptionTypes</code>
+ * on the <code>Method</code> passed to the <code>invoke</code> method
+ * <b>are</b> the exact set that can be thrown without resulting in an
+ * <code>UndeclaredThrowableException</code> being thrown.
+ * <li>{@link UndeclaredThrowableException} is used instead
+ * of <code>java.lang.reflect.UndeclaredThrowableException</code>.
+ * </ul>
+ * <p>
+ * @version $Id: Proxy.java,v 1.6 2004/06/24 21:15:19 herbyderby Exp $
+ */
+public class Proxy implements Serializable {
+ protected InvocationHandler h;
+
+ private static final CallbackFilter BAD_OBJECT_METHOD_FILTER = new CallbackFilter() {
+ public int accept(Method method, List<Method> allMethods) {
+ if (method.getDeclaringClass().getName().equals("java.lang.Object")) {
+ String name = method.getName();
+ if (!(name.equals("hashCode") ||
+ name.equals("equals") ||
+ name.equals("toString"))) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+ };
+
+ protected Proxy(InvocationHandler h) {
+ Enhancer.registerCallbacks(getClass(), new Callback[]{ h, null });
+ this.h = h;
+ }
+
+ // private for security of isProxyClass
+ private static class ProxyImpl extends Proxy {
+ protected ProxyImpl(InvocationHandler h) {
+ super(h);
+ }
+ }
+
+ public static InvocationHandler getInvocationHandler(Object proxy) {
+ if (!(proxy instanceof ProxyImpl)) {
+ throw new IllegalArgumentException("Object is not a proxy");
+ }
+ return ((Proxy)proxy).h;
+ }
+
+ public static Class getProxyClass(ClassLoader loader, Class[] interfaces) {
+ Enhancer e = new Enhancer();
+ e.setSuperclass(ProxyImpl.class);
+ e.setInterfaces(interfaces);
+ e.setCallbackTypes(new Class[]{
+ InvocationHandler.class,
+ NoOp.class,
+ });
+ e.setCallbackFilter(BAD_OBJECT_METHOD_FILTER);
+ e.setUseFactory(false);
+ return e.createClass();
+ }
+
+ public static boolean isProxyClass(Class cl) {
+ return cl.getSuperclass().equals(ProxyImpl.class);
+ }
+
+ public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) {
+ try {
+ Class clazz = getProxyClass(loader, interfaces);
+ return clazz.getConstructor(new Class[]{ InvocationHandler.class }).newInstance(new Object[]{ h });
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/ProxyRefDispatcher.java b/cglib-and-asm/src/org/mockito/cglib/proxy/ProxyRefDispatcher.java
new file mode 100644
index 0000000..4f30018
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/ProxyRefDispatcher.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.proxy;
+
+/**
+ * Dispatching {@link Enhancer} callback. This is the same as the
+ * {@link Dispatcher} except for the addition of an argument
+ * which references the proxy object.
+ */
+public interface ProxyRefDispatcher extends Callback {
+ /**
+ * Return the object which the original method invocation should
+ * be dispatched. This method is called for <b>every</b> method invocation.
+ * @param proxy a reference to the proxy (generated) object
+ * @return an object that can invoke the method
+ */
+ Object loadObject(Object proxy) throws Exception;
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/proxy/UndeclaredThrowableException.java b/cglib-and-asm/src/org/mockito/cglib/proxy/UndeclaredThrowableException.java
new file mode 100644
index 0000000..98b7ad7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/proxy/UndeclaredThrowableException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2002,2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mockito.cglib.proxy;
+
+import org.mockito.cglib.core.CodeGenerationException;
+
+/**
+ * Used by {@link Proxy} as a replacement for <code>java.lang.reflect.UndeclaredThrowableException</code>.
+ * @author Juozas Baliuka
+ */
+public class UndeclaredThrowableException extends CodeGenerationException {
+ /**
+ * Creates a new instance of <code>UndeclaredThrowableException</code> without detail message.
+ */
+ public UndeclaredThrowableException(Throwable t) {
+ super(t);
+ }
+
+ public Throwable getUndeclaredThrowable() {
+ return getCause();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/reflect/ConstructorDelegate.java b/cglib-and-asm/src/org/mockito/cglib/reflect/ConstructorDelegate.java
new file mode 100644
index 0000000..d6fe3b7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/reflect/ConstructorDelegate.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.reflect;
+
+import java.lang.reflect.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+/**
+ * @author Chris Nokleberg
+ * @version $Id: ConstructorDelegate.java,v 1.20 2006/03/05 02:43:19 herbyderby Exp $
+ */
+abstract public class ConstructorDelegate {
+ private static final ConstructorKey KEY_FACTORY =
+ (ConstructorKey)KeyFactory.create(ConstructorKey.class, KeyFactory.CLASS_BY_NAME);
+
+ interface ConstructorKey {
+ public Object newInstance(String declaring, String iface);
+ }
+
+ protected ConstructorDelegate() {
+ }
+
+ public static ConstructorDelegate create(Class targetClass, Class iface) {
+ Generator gen = new Generator();
+ gen.setTargetClass(targetClass);
+ gen.setInterface(iface);
+ return gen.create();
+ }
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(ConstructorDelegate.class.getName());
+ private static final Type CONSTRUCTOR_DELEGATE =
+ TypeUtils.parseType("org.mockito.cglib.reflect.ConstructorDelegate");
+
+ private Class iface;
+ private Class targetClass;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ public void setInterface(Class iface) {
+ this.iface = iface;
+ }
+
+ public void setTargetClass(Class targetClass) {
+ this.targetClass = targetClass;
+ }
+
+ public ConstructorDelegate create() {
+ setNamePrefix(targetClass.getName());
+ Object key = KEY_FACTORY.newInstance(iface.getName(), targetClass.getName());
+ return (ConstructorDelegate)super.create(key);
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return targetClass.getClassLoader();
+ }
+
+ public void generateClass(ClassVisitor v) {
+ setNamePrefix(targetClass.getName());
+
+ final Method newInstance = ReflectUtils.findNewInstance(iface);
+ if (!newInstance.getReturnType().isAssignableFrom(targetClass)) {
+ throw new IllegalArgumentException("incompatible return type");
+ }
+ final Constructor constructor;
+ try {
+ constructor = targetClass.getDeclaredConstructor(newInstance.getParameterTypes());
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("interface does not match any known constructor");
+ }
+
+ ClassEmitter ce = new ClassEmitter(v);
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ CONSTRUCTOR_DELEGATE,
+ new Type[]{ Type.getType(iface) },
+ Constants.SOURCE_FILE);
+ Type declaring = Type.getType(constructor.getDeclaringClass());
+ EmitUtils.null_constructor(ce);
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
+ ReflectUtils.getSignature(newInstance),
+ ReflectUtils.getExceptionTypes(newInstance));
+ e.new_instance(declaring);
+ e.dup();
+ e.load_args();
+ e.invoke_constructor(declaring, ReflectUtils.getSignature(constructor));
+ e.return_value();
+ e.end_method();
+ ce.end_class();
+ }
+
+ protected Object firstInstance(Class type) {
+ return ReflectUtils.newInstance(type);
+ }
+
+ protected Object nextInstance(Object instance) {
+ return instance;
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/reflect/FastClass.java b/cglib-and-asm/src/org/mockito/cglib/reflect/FastClass.java
new file mode 100644
index 0000000..3a6d241
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/reflect/FastClass.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.reflect;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+abstract public class FastClass
+{
+ private Class type;
+
+ protected FastClass() {
+ throw new Error("Using the FastClass empty constructor--please report to the cglib-devel mailing list");
+ }
+
+ protected FastClass(Class type) {
+ this.type = type;
+ }
+
+ public static FastClass create(Class type) {
+
+ return create(type.getClassLoader(),type);
+
+ }
+ public static FastClass create(ClassLoader loader, Class type) {
+ Generator gen = new Generator();
+ gen.setType(type);
+ gen.setClassLoader(loader);
+ return gen.create();
+ }
+
+ public static class Generator extends AbstractClassGenerator
+ {
+ private static final Source SOURCE = new Source(FastClass.class.getName());
+ private Class type;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ public void setType(Class type) {
+ this.type = type;
+ }
+
+ public FastClass create() {
+ setNamePrefix(type.getName());
+ return (FastClass)super.create(type.getName());
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return type.getClassLoader();
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ new FastClassEmitter(v, getClassName(), type);
+ }
+
+ protected Object firstInstance(Class type) {
+ return ReflectUtils.newInstance(type,
+ new Class[]{ Class.class },
+ new Object[]{ this.type });
+ }
+
+ protected Object nextInstance(Object instance) {
+ return instance;
+ }
+ }
+
+ public Object invoke(String name, Class[] parameterTypes, Object obj, Object[] args) throws InvocationTargetException {
+ return invoke(getIndex(name, parameterTypes), obj, args);
+ }
+
+ public Object newInstance() throws InvocationTargetException {
+ return newInstance(getIndex(Constants.EMPTY_CLASS_ARRAY), null);
+ }
+
+ public Object newInstance(Class[] parameterTypes, Object[] args) throws InvocationTargetException {
+ return newInstance(getIndex(parameterTypes), args);
+ }
+
+ public FastMethod getMethod(Method method) {
+ return new FastMethod(this, method);
+ }
+
+ public FastConstructor getConstructor(Constructor constructor) {
+ return new FastConstructor(this, constructor);
+ }
+
+ public FastMethod getMethod(String name, Class[] parameterTypes) {
+ try {
+ return getMethod(type.getMethod(name, parameterTypes));
+ } catch (NoSuchMethodException e) {
+ throw new NoSuchMethodError(e.getMessage());
+ }
+ }
+
+ public FastConstructor getConstructor(Class[] parameterTypes) {
+ try {
+ return getConstructor(type.getConstructor(parameterTypes));
+ } catch (NoSuchMethodException e) {
+ throw new NoSuchMethodError(e.getMessage());
+ }
+ }
+
+ public String getName() {
+ return type.getName();
+ }
+
+ public Class getJavaClass() {
+ return type;
+ }
+
+ public String toString() {
+ return type.toString();
+ }
+
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof FastClass)) {
+ return false;
+ }
+ return type.equals(((FastClass)o).type);
+ }
+
+ /**
+ * Return the index of the matching method. The index may be used
+ * later to invoke the method with less overhead. If more than one
+ * method matches (i.e. they differ by return type only), one is
+ * chosen arbitrarily.
+ * @see #invoke(int, Object, Object[])
+ * @param name the method name
+ * @param parameterTypes the parameter array
+ * @return the index, or <code>-1</code> if none is found.
+ */
+ abstract public int getIndex(String name, Class[] parameterTypes);
+
+ /**
+ * Return the index of the matching constructor. The index may be used
+ * later to create a new instance with less overhead.
+ * @see #newInstance(int, Object[])
+ * @param parameterTypes the parameter array
+ * @return the constructor index, or <code>-1</code> if none is found.
+ */
+ abstract public int getIndex(Class[] parameterTypes);
+
+ /**
+ * Invoke the method with the specified index.
+ * @see getIndex(name, Class[])
+ * @param index the method index
+ * @param obj the object the underlying method is invoked from
+ * @param args the arguments used for the method call
+ * @throws java.lang.reflect.InvocationTargetException if the underlying method throws an exception
+ */
+ abstract public Object invoke(int index, Object obj, Object[] args) throws InvocationTargetException;
+
+ /**
+ * Create a new instance using the specified constructor index and arguments.
+ * @see getIndex(Class[])
+ * @param index the constructor index
+ * @param args the arguments passed to the constructor
+ * @throws java.lang.reflect.InvocationTargetException if the constructor throws an exception
+ */
+ abstract public Object newInstance(int index, Object[] args) throws InvocationTargetException;
+
+ abstract public int getIndex(Signature sig);
+
+ /**
+ * Returns the maximum method index for this class.
+ */
+ abstract public int getMaxIndex();
+
+ protected static String getSignatureWithoutReturnType(String name, Class[] parameterTypes) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(name);
+ sb.append('(');
+ for (int i = 0; i < parameterTypes.length; i++) {
+ sb.append(Type.getDescriptor(parameterTypes[i]));
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/reflect/FastClassEmitter.java b/cglib-and-asm/src/org/mockito/cglib/reflect/FastClassEmitter.java
new file mode 100644
index 0000000..eb4f783
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/reflect/FastClassEmitter.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.reflect;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Label;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+class FastClassEmitter extends ClassEmitter {
+ private static final Signature CSTRUCT_CLASS =
+ TypeUtils.parseConstructor("Class");
+ private static final Signature METHOD_GET_INDEX =
+ TypeUtils.parseSignature("int getIndex(String, Class[])");
+ private static final Signature SIGNATURE_GET_INDEX =
+ new Signature("getIndex", Type.INT_TYPE, new Type[]{ Constants.TYPE_SIGNATURE });
+ private static final Signature TO_STRING =
+ TypeUtils.parseSignature("String toString()");
+ private static final Signature CONSTRUCTOR_GET_INDEX =
+ TypeUtils.parseSignature("int getIndex(Class[])");
+ private static final Signature INVOKE =
+ TypeUtils.parseSignature("Object invoke(int, Object, Object[])");
+ private static final Signature NEW_INSTANCE =
+ TypeUtils.parseSignature("Object newInstance(int, Object[])");
+ private static final Signature GET_MAX_INDEX =
+ TypeUtils.parseSignature("int getMaxIndex()");
+ private static final Signature GET_SIGNATURE_WITHOUT_RETURN_TYPE =
+ TypeUtils.parseSignature("String getSignatureWithoutReturnType(String, Class[])");
+ private static final Type FAST_CLASS =
+ TypeUtils.parseType("org.mockito.cglib.reflect.FastClass");
+ private static final Type ILLEGAL_ARGUMENT_EXCEPTION =
+ TypeUtils.parseType("IllegalArgumentException");
+ private static final Type INVOCATION_TARGET_EXCEPTION =
+ TypeUtils.parseType("java.lang.reflect.InvocationTargetException");
+ private static final Type[] INVOCATION_TARGET_EXCEPTION_ARRAY = { INVOCATION_TARGET_EXCEPTION };
+
+ public FastClassEmitter(ClassVisitor v, String className, Class type) {
+ super(v);
+
+ Type base = Type.getType(type);
+ begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, FAST_CLASS, null, Constants.SOURCE_FILE);
+
+ // constructor
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_CLASS, null);
+ e.load_this();
+ e.load_args();
+ e.super_invoke_constructor(CSTRUCT_CLASS);
+ e.return_value();
+ e.end_method();
+
+ VisibilityPredicate vp = new VisibilityPredicate(type, false);
+ List methods = ReflectUtils.addAllMethods(type, new ArrayList());
+ CollectionUtils.filter(methods, vp);
+ CollectionUtils.filter(methods, new DuplicatesPredicate());
+ List constructors = new ArrayList(Arrays.asList(type.getDeclaredConstructors()));
+ CollectionUtils.filter(constructors, vp);
+
+ // getIndex(String)
+ emitIndexBySignature(methods);
+
+ // getIndex(String, Class[])
+ emitIndexByClassArray(methods);
+
+ // getIndex(Class[])
+ e = begin_method(Constants.ACC_PUBLIC, CONSTRUCTOR_GET_INDEX, null);
+ e.load_args();
+ List info = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance());
+ EmitUtils.constructor_switch(e, info, new GetIndexCallback(e, info));
+ e.end_method();
+
+ // invoke(int, Object, Object[])
+ e = begin_method(Constants.ACC_PUBLIC, INVOKE, INVOCATION_TARGET_EXCEPTION_ARRAY);
+ e.load_arg(1);
+ e.checkcast(base);
+ e.load_arg(0);
+ invokeSwitchHelper(e, methods, 2, base);
+ e.end_method();
+
+ // newInstance(int, Object[])
+ e = begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, INVOCATION_TARGET_EXCEPTION_ARRAY);
+ e.new_instance(base);
+ e.dup();
+ e.load_arg(0);
+ invokeSwitchHelper(e, constructors, 1, base);
+ e.end_method();
+
+ // getMaxIndex()
+ e = begin_method(Constants.ACC_PUBLIC, GET_MAX_INDEX, null);
+ e.push(methods.size() - 1);
+ e.return_value();
+ e.end_method();
+
+ end_class();
+ }
+
+ // TODO: support constructor indices ("<init>")
+ private void emitIndexBySignature(List methods) {
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SIGNATURE_GET_INDEX, null);
+ List signatures = CollectionUtils.transform(methods, new Transformer() {
+ public Object transform(Object obj) {
+ return ReflectUtils.getSignature((Method)obj).toString();
+ }
+ });
+ e.load_arg(0);
+ e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING);
+ signatureSwitchHelper(e, signatures);
+ e.end_method();
+ }
+
+ private static final int TOO_MANY_METHODS = 100; // TODO
+ private void emitIndexByClassArray(List methods) {
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, METHOD_GET_INDEX, null);
+ if (methods.size() > TOO_MANY_METHODS) {
+ // hack for big classes
+ List signatures = CollectionUtils.transform(methods, new Transformer() {
+ public Object transform(Object obj) {
+ String s = ReflectUtils.getSignature((Method)obj).toString();
+ return s.substring(0, s.lastIndexOf(')') + 1);
+ }
+ });
+ e.load_args();
+ e.invoke_static(FAST_CLASS, GET_SIGNATURE_WITHOUT_RETURN_TYPE);
+ signatureSwitchHelper(e, signatures);
+ } else {
+ e.load_args();
+ List info = CollectionUtils.transform(methods, MethodInfoTransformer.getInstance());
+ EmitUtils.method_switch(e, info, new GetIndexCallback(e, info));
+ }
+ e.end_method();
+ }
+
+ private void signatureSwitchHelper(final CodeEmitter e, final List signatures) {
+ ObjectSwitchCallback callback = new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ // TODO: remove linear indexOf
+ e.push(signatures.indexOf(key));
+ e.return_value();
+ }
+ public void processDefault() {
+ e.push(-1);
+ e.return_value();
+ }
+ };
+ EmitUtils.string_switch(e,
+ (String[])signatures.toArray(new String[signatures.size()]),
+ Constants.SWITCH_STYLE_HASH,
+ callback);
+ }
+
+ private static void invokeSwitchHelper(final CodeEmitter e, List members, final int arg, final Type base) {
+ final List info = CollectionUtils.transform(members, MethodInfoTransformer.getInstance());
+ final Label illegalArg = e.make_label();
+ Block block = e.begin_block();
+ e.process_switch(getIntRange(info.size()), new ProcessSwitchCallback() {
+ public void processCase(int key, Label end) {
+ MethodInfo method = (MethodInfo)info.get(key);
+ Type[] types = method.getSignature().getArgumentTypes();
+ for (int i = 0; i < types.length; i++) {
+ e.load_arg(arg);
+ e.aaload(i);
+ e.unbox(types[i]);
+ }
+ // TODO: change method lookup process so MethodInfo will already reference base
+ // instead of superclass when superclass method is inaccessible
+ e.invoke(method, base);
+ if (!TypeUtils.isConstructor(method)) {
+ e.box(method.getSignature().getReturnType());
+ }
+ e.return_value();
+ }
+ public void processDefault() {
+ e.goTo(illegalArg);
+ }
+ });
+ block.end();
+ EmitUtils.wrap_throwable(block, INVOCATION_TARGET_EXCEPTION);
+ e.mark(illegalArg);
+ e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Cannot find matching method/constructor");
+ }
+
+ private static class GetIndexCallback implements ObjectSwitchCallback {
+ private CodeEmitter e;
+ private Map indexes = new HashMap();
+
+ public GetIndexCallback(CodeEmitter e, List methods) {
+ this.e = e;
+ int index = 0;
+ for (Iterator it = methods.iterator(); it.hasNext();) {
+ indexes.put(it.next(), new Integer(index++));
+ }
+ }
+
+ public void processCase(Object key, Label end) {
+ e.push(((Integer)indexes.get(key)).intValue());
+ e.return_value();
+ }
+
+ public void processDefault() {
+ e.push(-1);
+ e.return_value();
+ }
+ }
+
+ private static int[] getIntRange(int length) {
+ int[] range = new int[length];
+ for (int i = 0; i < length; i++) {
+ range[i] = i;
+ }
+ return range;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/reflect/FastConstructor.java b/cglib-and-asm/src/org/mockito/cglib/reflect/FastConstructor.java
new file mode 100644
index 0000000..292dde2
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/reflect/FastConstructor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.reflect;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+public class FastConstructor extends FastMember
+{
+ FastConstructor(FastClass fc, Constructor constructor) {
+ super(fc, constructor, fc.getIndex(constructor.getParameterTypes()));
+ }
+
+ public Class[] getParameterTypes() {
+ return ((Constructor)member).getParameterTypes();
+ }
+
+ public Class[] getExceptionTypes() {
+ return ((Constructor)member).getExceptionTypes();
+ }
+
+ public Object newInstance() throws InvocationTargetException {
+ return fc.newInstance(index, null);
+ }
+
+ public Object newInstance(Object[] args) throws InvocationTargetException {
+ return fc.newInstance(index, args);
+ }
+
+ public Constructor getJavaConstructor() {
+ return (Constructor)member;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/reflect/FastMember.java b/cglib-and-asm/src/org/mockito/cglib/reflect/FastMember.java
new file mode 100644
index 0000000..da644d3
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/reflect/FastMember.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.reflect;
+
+import java.lang.reflect.Member;
+
+abstract public class FastMember
+{
+ protected FastClass fc;
+ protected Member member;
+ protected int index;
+
+ protected FastMember(FastClass fc, Member member, int index) {
+ this.fc = fc;
+ this.member = member;
+ this.index = index;
+ }
+
+ abstract public Class[] getParameterTypes();
+ abstract public Class[] getExceptionTypes();
+
+ public int getIndex() {
+ return index;
+ }
+
+ public String getName() {
+ return member.getName();
+ }
+
+ public Class getDeclaringClass() {
+ return fc.getJavaClass();
+ }
+
+ public int getModifiers() {
+ return member.getModifiers();
+ }
+
+ public String toString() {
+ return member.toString();
+ }
+
+ public int hashCode() {
+ return member.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof FastMember)) {
+ return false;
+ }
+ return member.equals(((FastMember)o).member);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/reflect/FastMethod.java b/cglib-and-asm/src/org/mockito/cglib/reflect/FastMethod.java
new file mode 100644
index 0000000..1665c47
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/reflect/FastMethod.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.reflect;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class FastMethod extends FastMember
+{
+ FastMethod(FastClass fc, Method method) {
+ super(fc, method, helper(fc, method));
+ }
+
+ private static int helper(FastClass fc, Method method) {
+ int index = fc.getIndex(method.getName(), method.getParameterTypes());
+ if (index < 0) {
+ Class[] types = method.getParameterTypes();
+ System.err.println("hash=" + method.getName().hashCode() + " size=" + types.length);
+ for (int i = 0; i < types.length; i++) {
+ System.err.println(" types[" + i + "]=" + types[i].getName());
+ }
+ throw new IllegalArgumentException("Cannot find method " + method);
+ }
+ return index;
+ }
+
+ public Class getReturnType() {
+ return ((Method)member).getReturnType();
+ }
+
+ public Class[] getParameterTypes() {
+ return ((Method)member).getParameterTypes();
+ }
+
+ public Class[] getExceptionTypes() {
+ return ((Method)member).getExceptionTypes();
+ }
+
+ public Object invoke(Object obj, Object[] args) throws InvocationTargetException {
+ return fc.invoke(index, obj, args);
+ }
+
+ public Method getJavaMethod() {
+ return (Method)member;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/reflect/MethodDelegate.java b/cglib-and-asm/src/org/mockito/cglib/reflect/MethodDelegate.java
new file mode 100644
index 0000000..fb211e7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/reflect/MethodDelegate.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.reflect;
+
+import java.lang.reflect.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.*;
+import org.mockito.cglib.core.*;
+
+// TODO: don't require exact match for return type
+
+/**
+ * <b>DOCUMENTATION FROM APACHE AVALON DELEGATE CLASS</b>
+ *
+ * <p>
+ * Delegates are a typesafe pointer to another method. Since Java does not
+ * have language support for such a construct, this utility will construct
+ * a proxy that forwards method calls to any method with the same signature.
+ * This utility is inspired in part by the C# delegate mechanism. We
+ * implemented it in a Java-centric manner.
+ * </p>
+ *
+ * <h2>Delegate</h2>
+ * <p>
+ * Any interface with one method can become the interface for a delegate.
+ * Consider the example below:
+ * </p>
+ *
+ * <pre>
+ * public interface MainDelegate {
+ * int main(String[] args);
+ * }
+ * </pre>
+ *
+ * <p>
+ * The interface above is an example of an interface that can become a
+ * delegate. It has only one method, and the interface is public. In
+ * order to create a delegate for that method, all we have to do is
+ * call <code>MethodDelegate.create(this, "alternateMain", MainDelegate.class)</code>.
+ * The following program will show how to use it:
+ * </p>
+ *
+ * <pre>
+ * public class Main {
+ * public static int main( String[] args ) {
+ * Main newMain = new Main();
+ * MainDelegate start = (MainDelegate)
+ * MethodDelegate.create(newMain, "alternateMain", MainDelegate.class);
+ * return start.main( args );
+ * }
+ *
+ * public int alternateMain( String[] args ) {
+ * for (int i = 0; i < args.length; i++) {
+ * System.out.println( args[i] );
+ * }
+ * return args.length;
+ * }
+ * }
+ * </pre>
+ *
+ * <p>
+ * By themselves, delegates don't do much. Their true power lies in the fact that
+ * they can be treated like objects, and passed to other methods. In fact that is
+ * one of the key building blocks of building Intelligent Agents which in tern are
+ * the foundation of artificial intelligence. In the above program, we could have
+ * easily created the delegate to match the static <code>main</code> method by
+ * substituting the delegate creation call with this:
+ * <code>MethodDelegate.createStatic(getClass(), "main", MainDelegate.class)</code>.
+ * </p>
+ * <p>
+ * Another key use for Delegates is to register event listeners. It is much easier
+ * to have all the code for your events separated out into methods instead of individual
+ * classes. One of the ways Java gets around that is to create anonymous classes.
+ * They are particularly troublesome because many Debuggers do not know what to do
+ * with them. Anonymous classes tend to duplicate alot of code as well. We can
+ * use any interface with one declared method to forward events to any method that
+ * matches the signature (although the method name can be different).
+ * </p>
+ *
+ * <h3>Equality</h3>
+ * The criteria that we use to test if two delegates are equal are:
+ * <ul>
+ * <li>
+ * They both refer to the same instance. That is, the <code>instance</code>
+ * parameter passed to the newDelegate method was the same for both. The
+ * instances are compared with the identity equality operator, <code>==</code>.
+ * </li>
+ * <li>They refer to the same method as resolved by <code>Method.equals</code>.</li>
+ * </ul>
+ *
+ * @version $Id: MethodDelegate.java,v 1.25 2006/03/05 02:43:19 herbyderby Exp $
+ */
+abstract public class MethodDelegate {
+ private static final MethodDelegateKey KEY_FACTORY =
+ (MethodDelegateKey)KeyFactory.create(MethodDelegateKey.class, KeyFactory.CLASS_BY_NAME);
+
+ protected Object target;
+ protected String eqMethod;
+
+ interface MethodDelegateKey {
+ Object newInstance(Class delegateClass, String methodName, Class iface);
+ }
+
+ public static MethodDelegate createStatic(Class targetClass, String methodName, Class iface) {
+ Generator gen = new Generator();
+ gen.setTargetClass(targetClass);
+ gen.setMethodName(methodName);
+ gen.setInterface(iface);
+ return gen.create();
+ }
+
+ public static MethodDelegate create(Object target, String methodName, Class iface) {
+ Generator gen = new Generator();
+ gen.setTarget(target);
+ gen.setMethodName(methodName);
+ gen.setInterface(iface);
+ return gen.create();
+ }
+
+ public boolean equals(Object obj) {
+ MethodDelegate other = (MethodDelegate)obj;
+ return target == other.target && eqMethod.equals(other.eqMethod);
+ }
+
+ public int hashCode() {
+ return target.hashCode() ^ eqMethod.hashCode();
+ }
+
+ public Object getTarget() {
+ return target;
+ }
+
+ abstract public MethodDelegate newInstance(Object target);
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(MethodDelegate.class.getName());
+ private static final Type METHOD_DELEGATE =
+ TypeUtils.parseType("org.mockito.cglib.reflect.MethodDelegate");
+ private static final Signature NEW_INSTANCE =
+ new Signature("newInstance", METHOD_DELEGATE, new Type[]{ Constants.TYPE_OBJECT });
+
+ private Object target;
+ private Class targetClass;
+ private String methodName;
+ private Class iface;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ public void setTarget(Object target) {
+ this.target = target;
+ this.targetClass = target.getClass();
+ }
+
+ public void setTargetClass(Class targetClass) {
+ this.targetClass = targetClass;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public void setInterface(Class iface) {
+ this.iface = iface;
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return targetClass.getClassLoader();
+ }
+
+ public MethodDelegate create() {
+ setNamePrefix(targetClass.getName());
+ Object key = KEY_FACTORY.newInstance(targetClass, methodName, iface);
+ return (MethodDelegate)super.create(key);
+ }
+
+ protected Object firstInstance(Class type) {
+ return ((MethodDelegate)ReflectUtils.newInstance(type)).newInstance(target);
+ }
+
+ protected Object nextInstance(Object instance) {
+ return ((MethodDelegate)instance).newInstance(target);
+ }
+
+ public void generateClass(ClassVisitor v) throws NoSuchMethodException {
+ Method proxy = ReflectUtils.findInterfaceMethod(iface);
+ final Method method = targetClass.getMethod(methodName, proxy.getParameterTypes());
+ if (!proxy.getReturnType().isAssignableFrom(method.getReturnType())) {
+ throw new IllegalArgumentException("incompatible return types");
+ }
+
+ MethodInfo methodInfo = ReflectUtils.getMethodInfo(method);
+
+ boolean isStatic = TypeUtils.isStatic(methodInfo.getModifiers());
+ if ((target == null) ^ isStatic) {
+ throw new IllegalArgumentException("Static method " + (isStatic ? "not " : "") + "expected");
+ }
+
+ ClassEmitter ce = new ClassEmitter(v);
+ CodeEmitter e;
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ METHOD_DELEGATE,
+ new Type[]{ Type.getType(iface) },
+ Constants.SOURCE_FILE);
+ ce.declare_field(Constants.PRIVATE_FINAL_STATIC, "eqMethod", Constants.TYPE_STRING, null);
+ EmitUtils.null_constructor(ce);
+
+ // generate proxied method
+ MethodInfo proxied = ReflectUtils.getMethodInfo(iface.getDeclaredMethods()[0]);
+ e = EmitUtils.begin_method(ce, proxied, Constants.ACC_PUBLIC);
+ e.load_this();
+ e.super_getfield("target", Constants.TYPE_OBJECT);
+ e.checkcast(methodInfo.getClassInfo().getType());
+ e.load_args();
+ e.invoke(methodInfo);
+ e.return_value();
+ e.end_method();
+
+ // newInstance
+ e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null);
+ e.new_instance_this();
+ e.dup();
+ e.dup2();
+ e.invoke_constructor_this();
+ e.getfield("eqMethod");
+ e.super_putfield("eqMethod", Constants.TYPE_STRING);
+ e.load_arg(0);
+ e.super_putfield("target", Constants.TYPE_OBJECT);
+ e.return_value();
+ e.end_method();
+
+ // static initializer
+ e = ce.begin_static();
+ e.push(methodInfo.getSignature().toString());
+ e.putfield("eqMethod");
+ e.return_value();
+ e.end_method();
+
+ ce.end_class();
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/reflect/MulticastDelegate.java b/cglib-and-asm/src/org/mockito/cglib/reflect/MulticastDelegate.java
new file mode 100644
index 0000000..a180a74
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/reflect/MulticastDelegate.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.reflect;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+abstract public class MulticastDelegate implements Cloneable {
+ protected Object[] targets = {};
+
+ protected MulticastDelegate() {
+ }
+
+ public List getTargets() {
+ return new ArrayList(Arrays.asList(targets));
+ }
+
+ abstract public MulticastDelegate add(Object target);
+
+ protected MulticastDelegate addHelper(Object target) {
+ MulticastDelegate copy = newInstance();
+ copy.targets = new Object[targets.length + 1];
+ System.arraycopy(targets, 0, copy.targets, 0, targets.length);
+ copy.targets[targets.length] = target;
+ return copy;
+ }
+
+ public MulticastDelegate remove(Object target) {
+ for (int i = targets.length - 1; i >= 0; i--) {
+ if (targets[i].equals(target)) {
+ MulticastDelegate copy = newInstance();
+ copy.targets = new Object[targets.length - 1];
+ System.arraycopy(targets, 0, copy.targets, 0, i);
+ System.arraycopy(targets, i + 1, copy.targets, i, targets.length - i - 1);
+ return copy;
+ }
+ }
+ return this;
+ }
+
+ abstract public MulticastDelegate newInstance();
+
+ public static MulticastDelegate create(Class iface) {
+ Generator gen = new Generator();
+ gen.setInterface(iface);
+ return gen.create();
+ }
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(MulticastDelegate.class.getName());
+ private static final Type MULTICAST_DELEGATE =
+ TypeUtils.parseType("org.mockito.cglib.reflect.MulticastDelegate");
+ private static final Signature NEW_INSTANCE =
+ new Signature("newInstance", MULTICAST_DELEGATE, new Type[0]);
+ private static final Signature ADD_DELEGATE =
+ new Signature("add", MULTICAST_DELEGATE, new Type[]{ Constants.TYPE_OBJECT });
+ private static final Signature ADD_HELPER =
+ new Signature("addHelper", MULTICAST_DELEGATE, new Type[]{ Constants.TYPE_OBJECT });
+
+ private Class iface;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return iface.getClassLoader();
+ }
+
+ public void setInterface(Class iface) {
+ this.iface = iface;
+ }
+
+ public MulticastDelegate create() {
+ setNamePrefix(MulticastDelegate.class.getName());
+ return (MulticastDelegate)super.create(iface.getName());
+ }
+
+ public void generateClass(ClassVisitor cv) {
+ final MethodInfo method = ReflectUtils.getMethodInfo(ReflectUtils.findInterfaceMethod(iface));
+
+ ClassEmitter ce = new ClassEmitter(cv);
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ MULTICAST_DELEGATE,
+ new Type[]{ Type.getType(iface) },
+ Constants.SOURCE_FILE);
+ EmitUtils.null_constructor(ce);
+
+ // generate proxied method
+ emitProxy(ce, method);
+
+ // newInstance
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null);
+ e.new_instance_this();
+ e.dup();
+ e.invoke_constructor_this();
+ e.return_value();
+ e.end_method();
+
+ // add
+ e = ce.begin_method(Constants.ACC_PUBLIC, ADD_DELEGATE, null);
+ e.load_this();
+ e.load_arg(0);
+ e.checkcast(Type.getType(iface));
+ e.invoke_virtual_this(ADD_HELPER);
+ e.return_value();
+ e.end_method();
+
+ ce.end_class();
+ }
+
+ private void emitProxy(ClassEmitter ce, final MethodInfo method) {
+ final CodeEmitter e = EmitUtils.begin_method(ce, method, Constants.ACC_PUBLIC);
+ Type returnType = method.getSignature().getReturnType();
+ final boolean returns = returnType != Type.VOID_TYPE;
+ Local result = null;
+ if (returns) {
+ result = e.make_local(returnType);
+ e.zero_or_null(returnType);
+ e.store_local(result);
+ }
+ e.load_this();
+ e.super_getfield("targets", Constants.TYPE_OBJECT_ARRAY);
+ final Local result2 = result;
+ EmitUtils.process_array(e, Constants.TYPE_OBJECT_ARRAY, new ProcessArrayCallback() {
+ public void processElement(Type type) {
+ e.checkcast(Type.getType(iface));
+ e.load_args();
+ e.invoke(method);
+ if (returns) {
+ e.store_local(result2);
+ }
+ }
+ });
+ if (returns) {
+ e.load_local(result);
+ }
+ e.return_value();
+ e.end_method();
+ }
+
+ protected Object firstInstance(Class type) {
+ // make a new instance in case first object is used with a long list of targets
+ return ((MulticastDelegate)ReflectUtils.newInstance(type)).newInstance();
+ }
+
+ protected Object nextInstance(Object instance) {
+ return ((MulticastDelegate)instance).newInstance();
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassFilterTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassFilterTransformer.java
new file mode 100644
index 0000000..213b8ac
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassFilterTransformer.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.*;
+
+abstract public class AbstractClassFilterTransformer extends AbstractClassTransformer {
+ private ClassTransformer pass;
+ private ClassVisitor target;
+
+ public void setTarget(ClassVisitor target) {
+ super.setTarget(target);
+ pass.setTarget(target);
+ }
+
+ protected AbstractClassFilterTransformer(ClassTransformer pass) {
+ this.pass = pass;
+ }
+
+ abstract protected boolean accept(int version, int access, String name, String signature, String superName, String[] interfaces);
+
+ public void visit(int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ target = accept(version, access, name, signature, superName, interfaces) ? pass : cv;
+ target.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ public void visitSource(String source, String debug) {
+ target.visitSource(source, debug);
+ }
+
+ public void visitOuterClass(String owner, String name, String desc) {
+ target.visitOuterClass(owner, name, desc);
+ }
+
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return target.visitAnnotation(desc, visible);
+ }
+
+ public void visitAttribute(Attribute attr) {
+ target.visitAttribute(attr);
+ }
+
+ public void visitInnerClass(String name, String outerName, String innerName, int access) {
+ target.visitInnerClass(name, outerName, innerName, access);
+ }
+
+ public FieldVisitor visitField(int access,
+ String name,
+ String desc,
+ String signature,
+ Object value) {
+ return target.visitField(access, name, desc, signature, value);
+ }
+
+ public MethodVisitor visitMethod(int access,
+ String name,
+ String desc,
+ String signature,
+ String[] exceptions) {
+ return target.visitMethod(access, name, desc, signature, exceptions);
+ }
+
+ public void visitEnd() {
+ target.visitEnd();
+ target = null; // just to be safe
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassLoader.java b/cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassLoader.java
new file mode 100644
index 0000000..57846df
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassLoader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.ClassReader;
+import org.mockito.asm.ClassWriter;
+import org.mockito.asm.util.*;
+import org.mockito.cglib.core.ClassGenerator;
+import org.mockito.cglib.core.CodeGenerationException;
+import org.mockito.cglib.core.DebuggingClassWriter;
+
+import java.io.IOException;
+
+abstract public class AbstractClassLoader extends ClassLoader {
+ private ClassFilter filter;
+ private ClassLoader classPath;
+ private static java.security.ProtectionDomain DOMAIN ;
+
+ static{
+
+ DOMAIN = (java.security.ProtectionDomain)
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ return AbstractClassLoader.class.getProtectionDomain();
+ }
+ });
+ }
+
+ protected AbstractClassLoader(ClassLoader parent, ClassLoader classPath, ClassFilter filter) {
+ super(parent);
+ this.filter = filter;
+ this.classPath = classPath;
+ }
+
+ public Class loadClass(String name) throws ClassNotFoundException {
+
+ Class loaded = findLoadedClass(name);
+
+ if( loaded != null ){
+ if( loaded.getClassLoader() == this ){
+ return loaded;
+ }//else reload with this class loader
+ }
+
+ if (!filter.accept(name)) {
+ return super.loadClass(name);
+ }
+ ClassReader r;
+ try {
+
+ java.io.InputStream is = classPath.getResourceAsStream(
+ name.replace('.','/') + ".class"
+ );
+
+ if (is == null) {
+
+ throw new ClassNotFoundException(name);
+
+ }
+ try {
+
+ r = new ClassReader(is);
+
+ } finally {
+
+ is.close();
+
+ }
+ } catch (IOException e) {
+ throw new ClassNotFoundException(name + ":" + e.getMessage());
+ }
+
+ try {
+ ClassWriter w = new DebuggingClassWriter(ClassWriter.COMPUTE_MAXS);
+ getGenerator(r).generateClass(w);
+ byte[] b = w.toByteArray();
+ Class c = super.defineClass(name, b, 0, b.length, DOMAIN);
+ postProcess(c);
+ return c;
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Error e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ protected ClassGenerator getGenerator(ClassReader r) {
+ return new ClassReaderGenerator(r, attributes(), getFlags());
+ }
+
+ protected int getFlags() {
+ return 0;
+ }
+
+ protected Attribute[] attributes() {
+ return null;
+ }
+
+ protected void postProcess(Class c) {
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassTransformer.java
new file mode 100644
index 0000000..e157f95
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/AbstractClassTransformer.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.ClassAdapter;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.cglib.core.CodeGenerationException;
+
+abstract public class AbstractClassTransformer extends ClassAdapter implements ClassTransformer {
+ protected AbstractClassTransformer() {
+ super(null);
+ }
+
+ public void setTarget(ClassVisitor target) {
+ cv = target;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/AbstractProcessTask.java b/cglib-and-asm/src/org/mockito/cglib/transform/AbstractProcessTask.java
new file mode 100644
index 0000000..a0437e7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/AbstractProcessTask.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import java.io.File;
+import java.util.*;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+
+abstract public class AbstractProcessTask extends Task {
+ private Vector filesets = new Vector();
+
+ public void addFileset(FileSet set) {
+ filesets.addElement(set);
+ }
+
+ protected Collection getFiles() {
+ Map fileMap = new HashMap();
+ Project p = getProject();
+ for (int i = 0; i < filesets.size(); i++) {
+ FileSet fs = (FileSet)filesets.elementAt(i);
+ DirectoryScanner ds = fs.getDirectoryScanner(p);
+ String[] srcFiles = ds.getIncludedFiles();
+ File dir = fs.getDir(p);
+ for (int j = 0; j < srcFiles.length; j++) {
+ File src = new File(dir, srcFiles[j]);
+ fileMap.put(src.getAbsolutePath(), src);
+ }
+ }
+ return fileMap.values();
+ }
+
+
+
+ public void execute() throws BuildException {
+ beforeExecute();
+ for (Iterator it = getFiles().iterator(); it.hasNext();) {
+ try {
+ processFile((File)it.next());
+ } catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+ }
+
+ protected void beforeExecute() throws BuildException { }
+ abstract protected void processFile(File file) throws Exception;
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/AbstractTransformTask.java b/cglib-and-asm/src/org/mockito/cglib/transform/AbstractTransformTask.java
new file mode 100644
index 0000000..48779c6
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/AbstractTransformTask.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.mockito.cglib.transform;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.util.*;
+import java.util.zip.*;
+
+import org.apache.tools.ant.*;
+import org.mockito.asm.*;
+import org.mockito.cglib.core.*;
+
+abstract public class AbstractTransformTask extends AbstractProcessTask {
+ private static final int ZIP_MAGIC = 0x504B0304;
+
+ private static final int CLASS_MAGIC = 0xCAFEBABE;
+
+ private boolean verbose;
+
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ /**
+ * returns transformation for source class
+ *
+ * @param classInfo
+ * class information
+ * class name := classInfo[ 0 ]
+ * super class name := classInfo[ 1 ]
+ * interfaces := classInfo[ >1 ]
+ */
+ abstract protected ClassTransformer getClassTransformer(String[] classInfo);
+
+ protected Attribute[] attributes() {
+ return null;
+ }
+
+ protected void processFile(File file) throws Exception {
+
+ if (isClassFile(file)) {
+
+ processClassFile(file);
+
+ } else if (isJarFile(file)) {
+
+ processJarFile(file);
+
+ } else {
+
+ log("ignoring " + file.toURL(), Project.MSG_WARN);
+
+ }
+ }
+
+ /**
+ * @param file
+ * @throws Exception
+ * @throws FileNotFoundException
+ * @throws IOException
+ * @throws MalformedURLException
+ */
+ private void processClassFile(File file) throws Exception,
+ FileNotFoundException, IOException, MalformedURLException {
+
+ ClassReader reader = getClassReader(file);
+ String name[] = ClassNameReader.getClassInfo(reader);
+ ClassWriter w = new DebuggingClassWriter(ClassWriter.COMPUTE_MAXS);
+ ClassTransformer t = getClassTransformer(name);
+ if (t != null) {
+
+ if (verbose) {
+ log("processing " + file.toURL());
+ }
+ new TransformingClassGenerator(new ClassReaderGenerator(
+ getClassReader(file), attributes(), getFlags()), t)
+ .generateClass(w);
+ FileOutputStream fos = new FileOutputStream(file);
+ try {
+ fos.write(w.toByteArray());
+ } finally {
+ fos.close();
+ }
+
+ }
+
+ }
+
+ protected int getFlags() {
+ return 0;
+ }
+
+ private static ClassReader getClassReader(File file) throws Exception {
+ InputStream in = new BufferedInputStream(new FileInputStream(file));
+ try {
+ ClassReader r = new ClassReader(in);
+ return r;
+ } finally {
+ in.close();
+ }
+
+ }
+
+ protected boolean isClassFile(File file) throws IOException {
+
+ return checkMagic(file, CLASS_MAGIC);
+
+ }
+
+ protected void processJarFile(File file) throws Exception {
+
+ if (verbose) {
+ log("processing " + file.toURL());
+ }
+
+ File tempFile = File.createTempFile(file.getName(), null, new File(file
+ .getAbsoluteFile().getParent()));
+ try{
+
+ ZipInputStream zip = new ZipInputStream(new FileInputStream(file));
+ try {
+ FileOutputStream fout = new FileOutputStream(tempFile, false);
+ try{
+ ZipOutputStream out = new ZipOutputStream(fout);
+
+ ZipEntry entry;
+ while ((entry = zip.getNextEntry()) != null) {
+
+
+ byte bytes[] = getBytes(zip);
+
+ if (!entry.isDirectory()) {
+
+ DataInputStream din = new DataInputStream(
+ new ByteArrayInputStream(bytes)
+ );
+
+ if (din.readInt() == CLASS_MAGIC) {
+
+ bytes = process(bytes);
+
+ } else {
+ if (verbose) {
+ log("ignoring " + entry.toString());
+ }
+ }
+ }
+
+ ZipEntry outEntry = new ZipEntry(entry.getName());
+ outEntry.setMethod(entry.getMethod());
+ outEntry.setComment(entry.getComment());
+ outEntry.setSize(bytes.length);
+
+
+ if(outEntry.getMethod() == ZipEntry.STORED){
+ CRC32 crc = new CRC32();
+ crc.update(bytes);
+ outEntry.setCrc( crc.getValue() );
+ outEntry.setCompressedSize(bytes.length);
+ }
+ out.putNextEntry(outEntry);
+ out.write(bytes);
+ out.closeEntry();
+ zip.closeEntry();
+
+ }
+ out.close();
+ }finally{
+ fout.close();
+ }
+ } finally {
+ zip.close();
+ }
+
+
+ if(file.delete()){
+
+ File newFile = new File(tempFile.getAbsolutePath());
+
+ if(!newFile.renameTo(file)){
+ throw new IOException("can not rename " + tempFile + " to " + file);
+ }
+
+ }else{
+ throw new IOException("can not delete " + file);
+ }
+
+ }finally{
+
+ tempFile.delete();
+
+ }
+
+ }
+
+ /**
+ * @param bytes
+ * @return
+ * @throws IOException
+ * @throws Exception
+ */
+ private byte[] process(byte[] bytes) throws Exception {
+
+ ClassReader reader = new ClassReader(new ByteArrayInputStream(bytes));
+ String name[] = ClassNameReader.getClassInfo(reader);
+ ClassWriter w = new DebuggingClassWriter(ClassWriter.COMPUTE_MAXS);
+ ClassTransformer t = getClassTransformer(name);
+ if (t != null) {
+ if (verbose) {
+ log("processing " + name[0]);
+ }
+ new TransformingClassGenerator(new ClassReaderGenerator(
+ new ClassReader(new ByteArrayInputStream(bytes)),
+ attributes(), getFlags()), t).generateClass(w);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(w.toByteArray());
+ return out.toByteArray();
+ }
+ return bytes;
+ }
+
+ /**
+ * @param zip
+ * @return
+ * @throws IOException
+ */
+ private byte[] getBytes(ZipInputStream zip) throws IOException {
+
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ InputStream in = new BufferedInputStream(zip);
+ int b;
+ while ((b = in.read()) != -1) {
+ bout.write(b);
+ }
+ return bout.toByteArray();
+ }
+
+ private boolean checkMagic(File file, long magic) throws IOException {
+ DataInputStream in = new DataInputStream(new FileInputStream(file));
+ try {
+ int m = in.readInt();
+ return magic == m;
+ } finally {
+ in.close();
+ }
+ }
+
+ protected boolean isJarFile(File file) throws IOException {
+ return checkMagic(file, ZIP_MAGIC);
+ }
+
+} \ No newline at end of file
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/AnnotationVisitorTee.java b/cglib-and-asm/src/org/mockito/cglib/transform/AnnotationVisitorTee.java
new file mode 100644
index 0000000..3533440
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/AnnotationVisitorTee.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.AnnotationVisitor;
+
+public class AnnotationVisitorTee implements AnnotationVisitor {
+ private AnnotationVisitor av1, av2;
+
+ public static AnnotationVisitor getInstance(AnnotationVisitor av1, AnnotationVisitor av2) {
+ if (av1 == null)
+ return av2;
+ if (av2 == null)
+ return av1;
+ return new AnnotationVisitorTee(av1, av2);
+ }
+
+ public AnnotationVisitorTee(AnnotationVisitor av1, AnnotationVisitor av2) {
+ this.av1 = av1;
+ this.av2 = av2;
+ }
+
+ public void visit(String name, Object value) {
+ av2.visit(name, value);
+ av2.visit(name, value);
+ }
+
+ public void visitEnum(String name, String desc, String value) {
+ av1.visitEnum(name, desc, value);
+ av2.visitEnum(name, desc, value);
+ }
+
+ public AnnotationVisitor visitAnnotation(String name, String desc) {
+ return getInstance(av1.visitAnnotation(name, desc),
+ av2.visitAnnotation(name, desc));
+ }
+
+ public AnnotationVisitor visitArray(String name) {
+ return getInstance(av1.visitArray(name), av2.visitArray(name));
+ }
+
+ public void visitEnd() {
+ av1.visitEnd();
+ av2.visitEnd();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/ClassEmitterTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/ClassEmitterTransformer.java
new file mode 100644
index 0000000..3c92d47
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/ClassEmitterTransformer.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.cglib.core.ClassEmitter;
+
+abstract public class ClassEmitterTransformer extends ClassEmitter implements ClassTransformer {
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/ClassFilter.java b/cglib-and-asm/src/org/mockito/cglib/transform/ClassFilter.java
new file mode 100644
index 0000000..e453dfe
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/ClassFilter.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mockito.cglib.transform;
+
+/**
+ *
+ * @author baliuka
+ */
+public interface ClassFilter {
+
+ boolean accept(String className);
+
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/ClassFilterTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/ClassFilterTransformer.java
new file mode 100644
index 0000000..d9cdde2
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/ClassFilterTransformer.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.*;
+
+public class ClassFilterTransformer extends AbstractClassFilterTransformer {
+ private ClassFilter filter;
+
+ public ClassFilterTransformer(ClassFilter filter, ClassTransformer pass) {
+ super(pass);
+ this.filter = filter;
+ }
+
+ protected boolean accept(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ return filter.accept(name.replace('/', '.'));
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/ClassReaderGenerator.java b/cglib-and-asm/src/org/mockito/cglib/transform/ClassReaderGenerator.java
new file mode 100644
index 0000000..c4b8be7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/ClassReaderGenerator.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.ClassReader;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.cglib.core.ClassGenerator;
+
+public class ClassReaderGenerator implements ClassGenerator {
+ private final ClassReader r;
+ private final Attribute[] attrs;
+ private final int flags;
+
+ public ClassReaderGenerator(ClassReader r, int flags) {
+ this(r, null, flags);
+ }
+
+ public ClassReaderGenerator(ClassReader r, Attribute[] attrs, int flags) {
+ this.r = r;
+ this.attrs = (attrs != null) ? attrs : new Attribute[0];
+ this.flags = flags;
+ }
+
+ public void generateClass(ClassVisitor v) {
+ r.accept(v, attrs, flags);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformer.java
new file mode 100644
index 0000000..027cb26
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformer.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.ClassVisitor;
+
+public interface ClassTransformer extends ClassVisitor {
+ public void setTarget(ClassVisitor target);
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerChain.java b/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerChain.java
new file mode 100644
index 0000000..82f5a3b
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerChain.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.*;
+
+public class ClassTransformerChain extends AbstractClassTransformer {
+ private ClassTransformer[] chain;
+
+ public ClassTransformerChain(ClassTransformer[] chain) {
+ this.chain = (ClassTransformer[])chain.clone();
+ }
+
+ public void setTarget(ClassVisitor v) {
+ super.setTarget(chain[0]);
+ ClassVisitor next = v;
+ for (int i = chain.length - 1; i >= 0; i--) {
+ chain[i].setTarget(next);
+ next = chain[i];
+ }
+ }
+
+ public MethodVisitor visitMethod(int access,
+ String name,
+ String desc,
+ String signature,
+ String[] exceptions) {
+ return cv.visitMethod(access, name, desc, signature, exceptions);
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("ClassTransformerChain{");
+ for (int i = 0; i < chain.length; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(chain[i].toString());
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerFactory.java b/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerFactory.java
new file mode 100644
index 0000000..d5b80b7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerFactory.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+public interface ClassTransformerFactory {
+ ClassTransformer newInstance();
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerTee.java b/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerTee.java
new file mode 100644
index 0000000..64d90cb
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/ClassTransformerTee.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.ClassAdapter;
+import org.mockito.asm.ClassVisitor;
+
+public class ClassTransformerTee extends ClassAdapter implements ClassTransformer {
+ private ClassVisitor branch;
+
+ public ClassTransformerTee(ClassVisitor branch) {
+ super(null);
+ this.branch = branch;
+ }
+
+ public void setTarget(ClassVisitor target) {
+ cv = new ClassVisitorTee(branch, target);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/ClassVisitorTee.java b/cglib-and-asm/src/org/mockito/cglib/transform/ClassVisitorTee.java
new file mode 100644
index 0000000..6d4a016
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/ClassVisitorTee.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.*;
+
+public class ClassVisitorTee implements ClassVisitor {
+ private ClassVisitor cv1, cv2;
+
+ public ClassVisitorTee(ClassVisitor cv1, ClassVisitor cv2) {
+ this.cv1 = cv1;
+ this.cv2 = cv2;
+ }
+
+ public void visit(int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ cv1.visit(version, access, name, signature, superName, interfaces);
+ cv2.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ public void visitEnd() {
+ cv1.visitEnd();
+ cv2.visitEnd();
+ cv1 = cv2 = null;
+ }
+
+ public void visitInnerClass(String name, String outerName, String innerName, int access) {
+ cv1.visitInnerClass(name, outerName, innerName, access);
+ cv2.visitInnerClass(name, outerName, innerName, access);
+ }
+
+ public FieldVisitor visitField(int access,
+ String name,
+ String desc,
+ String signature,
+ Object value) {
+ FieldVisitor fv1 = cv1.visitField(access, name, desc, signature, value);
+ FieldVisitor fv2 = cv2.visitField(access, name, desc, signature, value);
+ if (fv1 == null)
+ return fv2;
+ if (fv2 == null)
+ return fv1;
+ return new FieldVisitorTee(fv1, fv2);
+ }
+
+
+ public MethodVisitor visitMethod(int access,
+ String name,
+ String desc,
+ String signature,
+ String[] exceptions) {
+ MethodVisitor mv1 = cv1.visitMethod(access, name, desc, signature, exceptions);
+ MethodVisitor mv2 = cv2.visitMethod(access, name, desc, signature, exceptions);
+ if (mv1 == null)
+ return mv2;
+ if (mv2 == null)
+ return mv1;
+ return new MethodVisitorTee(mv1, mv2);
+ }
+
+ public void visitSource(String source, String debug) {
+ cv1.visitSource(source, debug);
+ cv2.visitSource(source, debug);
+ }
+
+ public void visitOuterClass(String owner, String name, String desc) {
+ cv1.visitOuterClass(owner, name, desc);
+ cv2.visitOuterClass(owner, name, desc);
+ }
+
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return AnnotationVisitorTee.getInstance(cv1.visitAnnotation(desc, visible),
+ cv2.visitAnnotation(desc, visible));
+ }
+
+ public void visitAttribute(Attribute attrs) {
+ cv1.visitAttribute(attrs);
+ cv2.visitAttribute(attrs);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/FieldVisitorTee.java b/cglib-and-asm/src/org/mockito/cglib/transform/FieldVisitorTee.java
new file mode 100644
index 0000000..f30e162
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/FieldVisitorTee.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.AnnotationVisitor;
+import org.mockito.asm.Attribute;
+import org.mockito.asm.FieldVisitor;
+
+public class FieldVisitorTee implements FieldVisitor {
+ private FieldVisitor fv1, fv2;
+
+ public FieldVisitorTee(FieldVisitor fv1, FieldVisitor fv2) {
+ this.fv1 = fv1;
+ this.fv2 = fv2;
+ }
+
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return AnnotationVisitorTee.getInstance(fv1.visitAnnotation(desc, visible),
+ fv2.visitAnnotation(desc, visible));
+ }
+
+ public void visitAttribute(Attribute attr) {
+ fv1.visitAttribute(attr);
+ fv2.visitAttribute(attr);
+ }
+
+ public void visitEnd() {
+ fv1.visitEnd();
+ fv2.visitEnd();
+ }
+}
+
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/MethodFilter.java b/cglib-and-asm/src/org/mockito/cglib/transform/MethodFilter.java
new file mode 100644
index 0000000..667d3c3
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/MethodFilter.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.Attribute;
+
+public interface MethodFilter {
+ // TODO: pass class name too?
+ boolean accept(int access, String name, String desc, String signature, String[] exceptions);
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/MethodFilterTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/MethodFilterTransformer.java
new file mode 100644
index 0000000..90e13c7
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/MethodFilterTransformer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.*;
+
+public class MethodFilterTransformer extends AbstractClassTransformer {
+ private MethodFilter filter;
+ private ClassTransformer pass;
+ private ClassVisitor direct;
+
+ public MethodFilterTransformer(MethodFilter filter, ClassTransformer pass) {
+ this.filter = filter;
+ this.pass = pass;
+ super.setTarget(pass);
+ }
+
+ public MethodVisitor visitMethod(int access,
+ String name,
+ String desc,
+ String signature,
+ String[] exceptions) {
+ return (filter.accept(access, name, desc, signature, exceptions) ? pass : direct).visitMethod(access, name, desc, signature, exceptions);
+ }
+
+ public void setTarget(ClassVisitor target) {
+ pass.setTarget(target);
+ direct = target;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/MethodVisitorTee.java b/cglib-and-asm/src/org/mockito/cglib/transform/MethodVisitorTee.java
new file mode 100644
index 0000000..9f44a7f
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/MethodVisitorTee.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.*;
+
+public class MethodVisitorTee implements MethodVisitor {
+ private final MethodVisitor mv1;
+ private final MethodVisitor mv2;
+
+ public MethodVisitorTee(MethodVisitor mv1, MethodVisitor mv2) {
+ this.mv1 = mv1;
+ this.mv2 = mv2;
+ }
+
+ public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+ mv1.visitFrame(type, nLocal, local, nStack, stack);
+ mv2.visitFrame(type, nLocal, local, nStack, stack);
+ }
+
+ public AnnotationVisitor visitAnnotationDefault() {
+ return AnnotationVisitorTee.getInstance(mv1.visitAnnotationDefault(),
+ mv2.visitAnnotationDefault());
+ }
+
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return AnnotationVisitorTee.getInstance(mv1.visitAnnotation(desc, visible),
+ mv2.visitAnnotation(desc, visible));
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(int parameter,
+ String desc,
+ boolean visible) {
+ return AnnotationVisitorTee.getInstance(mv1.visitParameterAnnotation(parameter, desc, visible),
+ mv2.visitParameterAnnotation(parameter, desc, visible));
+ }
+
+ public void visitAttribute(Attribute attr) {
+ mv1.visitAttribute(attr);
+ mv2.visitAttribute(attr);
+ }
+
+ public void visitCode() {
+ mv1.visitCode();
+ mv2.visitCode();
+ }
+
+ public void visitInsn(int opcode) {
+ mv1.visitInsn(opcode);
+ mv2.visitInsn(opcode);
+ }
+
+ public void visitIntInsn(int opcode, int operand) {
+ mv1.visitIntInsn(opcode, operand);
+ mv2.visitIntInsn(opcode, operand);
+ }
+
+ public void visitVarInsn(int opcode, int var) {
+ mv1.visitVarInsn(opcode, var);
+ mv2.visitVarInsn(opcode, var);
+ }
+
+ public void visitTypeInsn(int opcode, String desc) {
+ mv1.visitTypeInsn(opcode, desc);
+ mv2.visitTypeInsn(opcode, desc);
+ }
+
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ mv1.visitFieldInsn(opcode, owner, name, desc);
+ mv2.visitFieldInsn(opcode, owner, name, desc);
+ }
+
+ public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+ mv1.visitMethodInsn(opcode, owner, name, desc);
+ mv2.visitMethodInsn(opcode, owner, name, desc);
+ }
+
+ public void visitJumpInsn(int opcode, Label label) {
+ mv1.visitJumpInsn(opcode, label);
+ mv2.visitJumpInsn(opcode, label);
+ }
+
+ public void visitLabel(Label label) {
+ mv1.visitLabel(label);
+ mv2.visitLabel(label);
+ }
+
+ public void visitLdcInsn(Object cst) {
+ mv1.visitLdcInsn(cst);
+ mv2.visitLdcInsn(cst);
+ }
+
+ public void visitIincInsn(int var, int increment) {
+ mv1.visitIincInsn(var, increment);
+ mv2.visitIincInsn(var, increment);
+ }
+
+ public void visitTableSwitchInsn(int min, int max, Label dflt, Label labels[]) {
+ mv1.visitTableSwitchInsn(min, max, dflt, labels);
+ mv2.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+
+ public void visitLookupSwitchInsn(Label dflt, int keys[], Label labels[]) {
+ mv1.visitLookupSwitchInsn(dflt, keys, labels);
+ mv2.visitLookupSwitchInsn(dflt, keys, labels);
+ }
+
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ mv1.visitMultiANewArrayInsn(desc, dims);
+ mv2.visitMultiANewArrayInsn(desc, dims);
+ }
+
+ public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+ mv1.visitTryCatchBlock(start, end, handler, type);
+ mv2.visitTryCatchBlock(start, end, handler, type);
+ }
+
+ public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
+ mv1.visitLocalVariable(name, desc, signature, start, end, index);
+ mv2.visitLocalVariable(name, desc, signature, start, end, index);
+ }
+
+ public void visitLineNumber(int line, Label start) {
+ mv1.visitLineNumber(line, start);
+ mv2.visitLineNumber(line, start);
+ }
+
+ public void visitMaxs(int maxStack, int maxLocals) {
+ mv1.visitMaxs(maxStack, maxLocals);
+ mv2.visitMaxs(maxStack, maxLocals);
+ }
+
+ public void visitEnd() {
+ mv1.visitEnd();
+ mv2.visitEnd();
+ }
+}
+
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/TransformingClassGenerator.java b/cglib-and-asm/src/org/mockito/cglib/transform/TransformingClassGenerator.java
new file mode 100644
index 0000000..722e88f
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/TransformingClassGenerator.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.cglib.core.ClassGenerator;
+import org.mockito.cglib.core.Transformer;
+
+public class TransformingClassGenerator implements ClassGenerator {
+ private ClassGenerator gen;
+ private ClassTransformer t;
+
+ public TransformingClassGenerator(ClassGenerator gen, ClassTransformer t) {
+ this.gen = gen;
+ this.t = t;
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ t.setTarget(v);
+ gen.generateClass(t);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/TransformingClassLoader.java b/cglib-and-asm/src/org/mockito/cglib/transform/TransformingClassLoader.java
new file mode 100644
index 0000000..a628649
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/TransformingClassLoader.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform;
+
+import java.util.*;
+
+import org.mockito.asm.*;
+import org.mockito.cglib.core.ClassGenerator;
+
+public class TransformingClassLoader extends AbstractClassLoader {
+ private ClassTransformerFactory t;
+
+ public TransformingClassLoader(ClassLoader parent, ClassFilter filter, ClassTransformerFactory t) {
+ super(parent, parent, filter);
+ this.t = t;
+ }
+
+ protected ClassGenerator getGenerator(ClassReader r) {
+ ClassTransformer t2 = (ClassTransformer)t.newInstance();
+ return new TransformingClassGenerator(super.getGenerator(r), t2);
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/AbstractInterceptFieldCallback.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AbstractInterceptFieldCallback.java
new file mode 100644
index 0000000..0baee23
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AbstractInterceptFieldCallback.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+/**
+ * @author Chris Nokleberg
+ */
+public class AbstractInterceptFieldCallback implements InterceptFieldCallback {
+
+ public int writeInt(Object obj, String name, int oldValue, int newValue) { return newValue; }
+ public char writeChar(Object obj, String name, char oldValue, char newValue) { return newValue; }
+ public byte writeByte(Object obj, String name, byte oldValue, byte newValue) { return newValue; }
+ public boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue) { return newValue; }
+ public short writeShort(Object obj, String name, short oldValue, short newValue) { return newValue; }
+ public float writeFloat(Object obj, String name, float oldValue, float newValue) { return newValue; }
+ public double writeDouble(Object obj, String name, double oldValue, double newValue) { return newValue; }
+ public long writeLong(Object obj, String name, long oldValue, long newValue) { return newValue; }
+ public Object writeObject(Object obj, String name, Object oldValue, Object newValue) { return newValue; }
+
+ public int readInt(Object obj, String name, int oldValue) { return oldValue; }
+ public char readChar(Object obj, String name, char oldValue) { return oldValue; }
+ public byte readByte(Object obj, String name, byte oldValue) { return oldValue; }
+ public boolean readBoolean(Object obj, String name, boolean oldValue) { return oldValue; }
+ public short readShort(Object obj, String name, short oldValue) { return oldValue; }
+ public float readFloat(Object obj, String name, float oldValue) { return oldValue; }
+ public double readDouble(Object obj, String name, double oldValue) { return oldValue; }
+ public long readLong(Object obj, String name, long oldValue) { return oldValue; }
+ public Object readObject(Object obj, String name, Object oldValue) { return oldValue; }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/AccessFieldTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AccessFieldTransformer.java
new file mode 100644
index 0000000..ec81215
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AccessFieldTransformer.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+import org.mockito.cglib.transform.*;
+
+public class AccessFieldTransformer extends ClassEmitterTransformer {
+ private Callback callback;
+
+ public AccessFieldTransformer(Callback callback) {
+ this.callback = callback;
+ }
+
+ public interface Callback {
+ String getPropertyName(Type owner, String fieldName);
+ }
+
+ public void declare_field(int access, final String name, Type type, Object value) {
+ super.declare_field(access, name, type, value);
+
+ String property = TypeUtils.upperFirst(callback.getPropertyName(getClassType(), name));
+ if (property != null) {
+ CodeEmitter e;
+ e = begin_method(Constants.ACC_PUBLIC,
+ new Signature("get" + property,
+ type,
+ Constants.TYPES_EMPTY),
+ null);
+ e.load_this();
+ e.getfield(name);
+ e.return_value();
+ e.end_method();
+
+ e = begin_method(Constants.ACC_PUBLIC,
+ new Signature("set" + property,
+ Type.VOID_TYPE,
+ new Type[]{ type }),
+ null);
+ e.load_this();
+ e.load_arg(0);
+ e.putfield(name);
+ e.return_value();
+ e.end_method();
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddDelegateTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddDelegateTransformer.java
new file mode 100644
index 0000000..6c39ddd
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddDelegateTransformer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+import org.mockito.cglib.transform.*;
+
+/**
+ * @author Juozas Baliuka
+ */
+public class AddDelegateTransformer extends ClassEmitterTransformer {
+ private static final String DELEGATE = "$CGLIB_DELEGATE";
+ private static final Signature CSTRUCT_OBJECT =
+ TypeUtils.parseSignature("void <init>(Object)");
+
+ private Class[] delegateIf;
+ private Class delegateImpl;
+ private Type delegateType;
+
+ /** Creates a new instance of AddDelegateTransformer */
+ public AddDelegateTransformer(Class delegateIf[], Class delegateImpl) {
+ try {
+ delegateImpl.getConstructor(new Class[]{ Object.class });
+ this.delegateIf = delegateIf;
+ this.delegateImpl = delegateImpl;
+ delegateType = Type.getType(delegateImpl);
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String sourceFile) {
+
+ if(!TypeUtils.isInterface(access)){
+
+ Type[] all = TypeUtils.add(interfaces, TypeUtils.getTypes(delegateIf));
+ super.begin_class(version, access, className, superType, all, sourceFile);
+
+ declare_field(Constants.ACC_PRIVATE | Constants.ACC_TRANSIENT,
+ DELEGATE,
+ delegateType,
+ null);
+ for (int i = 0; i < delegateIf.length; i++) {
+ Method[] methods = delegateIf[i].getMethods();
+ for (int j = 0; j < methods.length; j++) {
+ if (Modifier.isAbstract(methods[j].getModifiers())) {
+ addDelegate(methods[j]);
+ }
+ }
+ }
+ }else{
+ super.begin_class(version, access, className, superType, interfaces, sourceFile);
+ }
+ }
+
+ public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) {
+ final CodeEmitter e = super.begin_method(access, sig, exceptions);
+ if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) {
+ return new CodeEmitter(e) {
+ private boolean transformInit = true;
+ public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+ super.visitMethodInsn(opcode, owner, name, desc);
+ if (transformInit && opcode == Constants.INVOKESPECIAL) {
+ load_this();
+ new_instance(delegateType);
+ dup();
+ load_this();
+ invoke_constructor(delegateType, CSTRUCT_OBJECT);
+ putfield(DELEGATE);
+ transformInit = false;
+ }
+ }
+ };
+ }
+ return e;
+ }
+
+ private void addDelegate(Method m) {
+ Method delegate;
+ try {
+ delegate = delegateImpl.getMethod(m.getName(), m.getParameterTypes());
+ if (!delegate.getReturnType().getName().equals(m.getReturnType().getName())){
+ throw new IllegalArgumentException("Invalid delegate signature " + delegate);
+ }
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+
+ final Signature sig = ReflectUtils.getSignature(m);
+ Type[] exceptions = TypeUtils.getTypes(m.getExceptionTypes());
+ CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, sig, exceptions);
+ e.load_this();
+ e.getfield(DELEGATE);
+ e.load_args();
+ e.invoke_virtual(delegateType, sig);
+ e.return_value();
+ e.end_method();
+ }
+}
+
+
+
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddInitTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddInitTransformer.java
new file mode 100644
index 0000000..b403c7d
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddInitTransformer.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+import java.lang.reflect.Method;
+
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.CodeEmitter;
+import org.mockito.cglib.core.Constants;
+import org.mockito.cglib.core.MethodInfo;
+import org.mockito.cglib.core.ReflectUtils;
+import org.mockito.cglib.core.Signature;
+import org.mockito.cglib.transform.ClassEmitterTransformer;
+
+/**
+ * @author Mark Hobson
+ */
+public class AddInitTransformer extends ClassEmitterTransformer {
+ private MethodInfo info;
+
+ public AddInitTransformer(Method method) {
+ info = ReflectUtils.getMethodInfo(method);
+
+ Type[] types = info.getSignature().getArgumentTypes();
+ if (types.length != 1 ||
+ !types[0].equals(Constants.TYPE_OBJECT) ||
+ !info.getSignature().getReturnType().equals(Type.VOID_TYPE)) {
+ throw new IllegalArgumentException(method + " illegal signature");
+ }
+ }
+
+ public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) {
+ final CodeEmitter emitter = super.begin_method(access, sig, exceptions);
+ if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) {
+ return new CodeEmitter(emitter) {
+ public void visitInsn(int opcode) {
+ if (opcode == Constants.RETURN) {
+ load_this();
+ invoke(info);
+ }
+ super.visitInsn(opcode);
+ }
+ };
+ }
+ return emitter;
+ }
+}
+
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddPropertyTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddPropertyTransformer.java
new file mode 100644
index 0000000..49ada76
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddPropertyTransformer.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+import java.util.*;
+
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+import org.mockito.cglib.transform.*;
+
+public class AddPropertyTransformer extends ClassEmitterTransformer {
+ private final String[] names;
+ private final Type[] types;
+
+ public AddPropertyTransformer(Map props) {
+ int size = props.size();
+ names = (String[])props.keySet().toArray(new String[size]);
+ types = new Type[size];
+ for (int i = 0; i < size; i++) {
+ types[i] = (Type)props.get(names[i]);
+ }
+ }
+
+ public AddPropertyTransformer(String[] names, Type[] types) {
+ this.names = names;
+ this.types = types;
+ }
+
+ public void end_class() {
+ if (!TypeUtils.isAbstract(getAccess())) {
+ EmitUtils.add_properties(this, names, types);
+ }
+ super.end_class();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddStaticInitTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddStaticInitTransformer.java
new file mode 100644
index 0000000..1753b2a
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/AddStaticInitTransformer.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2003,2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+import java.lang.reflect.Method;
+
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+import org.mockito.cglib.transform.*;
+
+/**
+ * @author Juozas Baliuka, Chris Nokleberg
+ */
+public class AddStaticInitTransformer extends ClassEmitterTransformer {
+ private MethodInfo info;
+
+ public AddStaticInitTransformer(Method classInit) {
+ info = ReflectUtils.getMethodInfo(classInit);
+ if (!TypeUtils.isStatic(info.getModifiers())) {
+ throw new IllegalArgumentException(classInit + " is not static");
+ }
+ Type[] types = info.getSignature().getArgumentTypes();
+ if (types.length != 1 ||
+ !types[0].equals(Constants.TYPE_CLASS) ||
+ !info.getSignature().getReturnType().equals(Type.VOID_TYPE)) {
+ throw new IllegalArgumentException(classInit + " illegal signature");
+ }
+ }
+
+ protected void init() {
+ if (!TypeUtils.isInterface(getAccess())) {
+ CodeEmitter e = getStaticHook();
+ EmitUtils.load_class_this(e);
+ e.invoke(info);
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/FieldProvider.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/FieldProvider.java
new file mode 100644
index 0000000..315e7e5
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/FieldProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+public interface FieldProvider {
+
+ String[] getFieldNames();
+
+ Class[] getFieldTypes();
+
+ void setField(int index, Object value);
+
+ Object getField(int index);
+
+
+ void setField(String name, Object value);
+
+ Object getField(String name);
+
+
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/FieldProviderTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/FieldProviderTransformer.java
new file mode 100644
index 0000000..8695069
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/FieldProviderTransformer.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+import java.util.*;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.Label;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+import org.mockito.cglib.transform.*;
+
+public class FieldProviderTransformer extends ClassEmitterTransformer {
+
+ private static final String FIELD_NAMES = "CGLIB$FIELD_NAMES";
+ private static final String FIELD_TYPES = "CGLIB$FIELD_TYPES";
+
+ private static final Type FIELD_PROVIDER =
+ TypeUtils.parseType("org.mockito.cglib.transform.impl.FieldProvider");
+ private static final Type ILLEGAL_ARGUMENT_EXCEPTION =
+ TypeUtils.parseType("IllegalArgumentException");
+ private static final Signature PROVIDER_GET =
+ TypeUtils.parseSignature("Object getField(String)");
+ private static final Signature PROVIDER_SET =
+ TypeUtils.parseSignature("void setField(String, Object)");
+ private static final Signature PROVIDER_SET_BY_INDEX =
+ TypeUtils.parseSignature("void setField(int, Object)");
+ private static final Signature PROVIDER_GET_BY_INDEX =
+ TypeUtils.parseSignature("Object getField(int)");
+ private static final Signature PROVIDER_GET_TYPES =
+ TypeUtils.parseSignature("Class[] getFieldTypes()");
+ private static final Signature PROVIDER_GET_NAMES =
+ TypeUtils.parseSignature("String[] getFieldNames()");
+
+ private int access;
+ private Map fields;
+
+ public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String sourceFile) {
+ if (!TypeUtils.isAbstract(access)) {
+ interfaces = TypeUtils.add(interfaces, FIELD_PROVIDER);
+ }
+ this.access = access;
+ fields = new HashMap();
+ super.begin_class(version, access, className, superType, interfaces, sourceFile);
+ }
+
+ public void declare_field(int access, String name, Type type, Object value) {
+ super.declare_field(access, name, type, value);
+
+ if (!TypeUtils.isStatic(access)) {
+ fields.put(name, type);
+ }
+ }
+
+ public void end_class() {
+ if (!TypeUtils.isInterface(access)) {
+ try {
+ generate();
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+ super.end_class();
+ }
+
+ private void generate() throws Exception {
+ final String[] names = (String[])fields.keySet().toArray(new String[fields.size()]);
+
+ int indexes[] = new int[names.length];
+ for (int i = 0; i < indexes.length; i++) {
+ indexes[i] = i;
+ }
+
+ super.declare_field(Constants.PRIVATE_FINAL_STATIC, FIELD_NAMES, Constants.TYPE_STRING_ARRAY, null);
+ super.declare_field(Constants.PRIVATE_FINAL_STATIC, FIELD_TYPES, Constants.TYPE_CLASS_ARRAY, null);
+
+ // use separate methods here because each process switch inner class needs a final CodeEmitter
+ initFieldProvider(names);
+ getNames();
+ getTypes();
+ getField(names);
+ setField(names);
+ setByIndex(names, indexes);
+ getByIndex(names, indexes);
+ }
+
+ private void initFieldProvider(String[] names) {
+ CodeEmitter e = getStaticHook();
+ EmitUtils.push_object(e, names);
+ e.putstatic(getClassType(), FIELD_NAMES, Constants.TYPE_STRING_ARRAY);
+
+ e.push(names.length);
+ e.newarray(Constants.TYPE_CLASS);
+ e.dup();
+ for(int i = 0; i < names.length; i++ ){
+ e.dup();
+ e.push(i);
+ Type type = (Type)fields.get(names[i]);
+ EmitUtils.load_class(e, type);
+ e.aastore();
+ }
+ e.putstatic(getClassType(), FIELD_TYPES, Constants.TYPE_CLASS_ARRAY);
+ }
+
+ private void getNames() {
+ CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_NAMES, null);
+ e.getstatic(getClassType(), FIELD_NAMES, Constants.TYPE_STRING_ARRAY);
+ e.return_value();
+ e.end_method();
+ }
+
+ private void getTypes() {
+ CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_TYPES, null);
+ e.getstatic(getClassType(), FIELD_TYPES, Constants.TYPE_CLASS_ARRAY);
+ e.return_value();
+ e.end_method();
+ }
+
+ private void setByIndex(final String[] names, final int[] indexes) throws Exception {
+ final CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_SET_BY_INDEX, null);
+ e.load_this();
+ e.load_arg(1);
+ e.load_arg(0);
+ e.process_switch(indexes, new ProcessSwitchCallback() {
+ public void processCase(int key, Label end) throws Exception {
+ Type type = (Type)fields.get(names[key]);
+ e.unbox(type);
+ e.putfield(names[key]);
+ e.return_value();
+ }
+ public void processDefault() throws Exception {
+ e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field index");
+ }
+ });
+ e.end_method();
+ }
+
+ private void getByIndex(final String[] names, final int[] indexes) throws Exception {
+ final CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_BY_INDEX, null);
+ e.load_this();
+ e.load_arg(0);
+ e.process_switch(indexes, new ProcessSwitchCallback() {
+ public void processCase(int key, Label end) throws Exception {
+ Type type = (Type)fields.get(names[key]);
+ e.getfield(names[key]);
+ e.box(type);
+ e.return_value();
+ }
+ public void processDefault() throws Exception {
+ e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field index");
+ }
+ });
+ e.end_method();
+ }
+
+ // TODO: if this is used to enhance class files SWITCH_STYLE_TRIE should be used
+ // to avoid JVM hashcode implementation incompatibilities
+ private void getField(String[] names) throws Exception {
+ final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, PROVIDER_GET, null);
+ e.load_this();
+ e.load_arg(0);
+ EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ Type type = (Type)fields.get(key);
+ e.getfield((String)key);
+ e.box(type);
+ e.return_value();
+ }
+ public void processDefault() {
+ e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field name");
+ }
+ });
+ e.end_method();
+ }
+
+ private void setField(String[] names) throws Exception {
+ final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, PROVIDER_SET, null);
+ e.load_this();
+ e.load_arg(1);
+ e.load_arg(0);
+ EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ Type type = (Type)fields.get(key);
+ e.unbox(type);
+ e.putfield((String)key);
+ e.return_value();
+ }
+ public void processDefault() {
+ e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field name");
+ }
+ });
+ e.end_method();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldCallback.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldCallback.java
new file mode 100644
index 0000000..06d4140
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldCallback.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+/**
+ * @author Juozas Baliuka
+ */
+public interface InterceptFieldCallback {
+
+ int writeInt(Object obj, String name, int oldValue, int newValue);
+ char writeChar(Object obj, String name, char oldValue, char newValue);
+ byte writeByte(Object obj, String name, byte oldValue, byte newValue);
+ boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue);
+ short writeShort(Object obj, String name, short oldValue, short newValue);
+ float writeFloat(Object obj, String name, float oldValue, float newValue);
+ double writeDouble(Object obj, String name, double oldValue, double newValue);
+ long writeLong(Object obj, String name, long oldValue, long newValue);
+ Object writeObject(Object obj, String name, Object oldValue, Object newValue);
+
+ int readInt(Object obj, String name, int oldValue);
+ char readChar(Object obj, String name, char oldValue);
+ byte readByte(Object obj, String name, byte oldValue);
+ boolean readBoolean(Object obj, String name, boolean oldValue);
+ short readShort(Object obj, String name, short oldValue);
+ float readFloat(Object obj, String name, float oldValue);
+ double readDouble(Object obj, String name, double oldValue);
+ long readLong(Object obj, String name, long oldValue);
+ Object readObject(Object obj, String name, Object oldValue);
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldEnabled.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldEnabled.java
new file mode 100644
index 0000000..bd92045
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldEnabled.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+public interface InterceptFieldEnabled {
+ void setInterceptFieldCallback(InterceptFieldCallback callback);
+ InterceptFieldCallback getInterceptFieldCallback();
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldFilter.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldFilter.java
new file mode 100644
index 0000000..31d4c1e
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldFilter.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+import org.mockito.asm.Type;
+
+public interface InterceptFieldFilter {
+ boolean acceptRead(Type owner, String name);
+ boolean acceptWrite(Type owner, String name);
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldTransformer.java
new file mode 100644
index 0000000..180571f
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/InterceptFieldTransformer.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Label;
+import org.mockito.asm.MethodAdapter;
+import org.mockito.asm.MethodVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+import org.mockito.cglib.transform.*;
+
+/**
+ * @author Juozas Baliuka, Chris Nokleberg
+ */
+public class InterceptFieldTransformer extends ClassEmitterTransformer {
+ private static final String CALLBACK_FIELD = "$CGLIB_READ_WRITE_CALLBACK";
+ private static final Type CALLBACK =
+ TypeUtils.parseType("org.mockito.cglib.transform.impl.InterceptFieldCallback");
+ private static final Type ENABLED =
+ TypeUtils.parseType("org.mockito.cglib.transform.impl.InterceptFieldEnabled");
+ private static final Signature ENABLED_SET =
+ new Signature("setInterceptFieldCallback", Type.VOID_TYPE, new Type[]{ CALLBACK });
+ private static final Signature ENABLED_GET =
+ new Signature("getInterceptFieldCallback", CALLBACK, new Type[0]);
+
+ private InterceptFieldFilter filter;
+
+ public InterceptFieldTransformer(InterceptFieldFilter filter) {
+ this.filter = filter;
+ }
+
+ public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String sourceFile) {
+ if (!TypeUtils.isInterface(access)) {
+ super.begin_class(version, access, className, superType, TypeUtils.add(interfaces, ENABLED), sourceFile);
+
+ super.declare_field(Constants.ACC_PRIVATE | Constants.ACC_TRANSIENT,
+ CALLBACK_FIELD,
+ CALLBACK,
+ null);
+
+ CodeEmitter e;
+ e = super.begin_method(Constants.ACC_PUBLIC, ENABLED_GET, null);
+ e.load_this();
+ e.getfield(CALLBACK_FIELD);
+ e.return_value();
+ e.end_method();
+
+ e = super.begin_method(Constants.ACC_PUBLIC, ENABLED_SET, null);
+ e.load_this();
+ e.load_arg(0);
+ e.putfield(CALLBACK_FIELD);
+ e.return_value();
+ e.end_method();
+ } else {
+ super.begin_class(version, access, className, superType, interfaces, sourceFile);
+ }
+ }
+
+ public void declare_field(int access, String name, Type type, Object value) {
+ super.declare_field(access, name, type, value);
+ if (!TypeUtils.isStatic(access)) {
+ if (filter.acceptRead(getClassType(), name)) {
+ addReadMethod(name, type);
+ }
+ if (filter.acceptWrite(getClassType(), name)) {
+ addWriteMethod(name, type);
+ }
+ }
+ }
+
+ private void addReadMethod(String name, Type type) {
+ CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC,
+ readMethodSig(name, type.getDescriptor()),
+ null);
+ e.load_this();
+ e.getfield(name);
+ e.load_this();
+ e.invoke_interface(ENABLED,ENABLED_GET);
+ Label intercept = e.make_label();
+ e.ifnonnull(intercept);
+ e.return_value();
+
+ e.mark(intercept);
+ Local result = e.make_local(type);
+ e.store_local(result);
+ e.load_this();
+ e.invoke_interface(ENABLED,ENABLED_GET);
+ e.load_this();
+ e.push(name);
+ e.load_local(result);
+ e.invoke_interface(CALLBACK, readCallbackSig(type));
+ if (!TypeUtils.isPrimitive(type)) {
+ e.checkcast(type);
+ }
+ e.return_value();
+ e.end_method();
+ }
+
+ private void addWriteMethod(String name, Type type) {
+ CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC,
+ writeMethodSig(name, type.getDescriptor()),
+ null);
+ e.load_this();
+ e.dup();
+ e.invoke_interface(ENABLED,ENABLED_GET);
+ Label skip = e.make_label();
+ e.ifnull(skip);
+
+ e.load_this();
+ e.invoke_interface(ENABLED,ENABLED_GET);
+ e.load_this();
+ e.push(name);
+ e.load_this();
+ e.getfield(name);
+ e.load_arg(0);
+ e.invoke_interface(CALLBACK, writeCallbackSig(type));
+ if (!TypeUtils.isPrimitive(type)) {
+ e.checkcast(type);
+ }
+ Label go = e.make_label();
+ e.goTo(go);
+ e.mark(skip);
+ e.load_arg(0);
+ e.mark(go);
+ e.putfield(name);
+ e.return_value();
+ e.end_method();
+ }
+
+ public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) {
+ return new CodeEmitter(super.begin_method(access, sig, exceptions)) {
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ Type towner = TypeUtils.fromInternalName(owner);
+ switch (opcode) {
+ case Constants.GETFIELD:
+ if (filter.acceptRead(towner, name)) {
+ helper(towner, readMethodSig(name, desc));
+ return;
+ }
+ break;
+ case Constants.PUTFIELD:
+ if (filter.acceptWrite(towner, name)) {
+ helper(towner, writeMethodSig(name, desc));
+ return;
+ }
+ break;
+ }
+ super.visitFieldInsn(opcode, owner, name, desc);
+ }
+
+ private void helper(Type owner, Signature sig) {
+ invoke_virtual(owner, sig);
+ }
+ };
+ }
+
+ private static Signature readMethodSig(String name, String desc) {
+ return new Signature("$cglib_read_" + name, "()" + desc);
+ }
+
+ private static Signature writeMethodSig(String name, String desc) {
+ return new Signature("$cglib_write_" + name, "(" + desc + ")V");
+ }
+
+ private static Signature readCallbackSig(Type type) {
+ Type remap = remap(type);
+ return new Signature("read" + callbackName(remap),
+ remap,
+ new Type[]{ Constants.TYPE_OBJECT,
+ Constants.TYPE_STRING,
+ remap });
+ }
+
+ private static Signature writeCallbackSig(Type type) {
+ Type remap = remap(type);
+ return new Signature("write" + callbackName(remap),
+ remap,
+ new Type[]{ Constants.TYPE_OBJECT,
+ Constants.TYPE_STRING,
+ remap,
+ remap });
+ }
+
+ private static Type remap(Type type) {
+ switch (type.getSort()) {
+ case Type.OBJECT:
+ case Type.ARRAY:
+ return Constants.TYPE_OBJECT;
+ default:
+ return type;
+ }
+ }
+
+ private static String callbackName(Type type) {
+ return (type == Constants.TYPE_OBJECT) ?
+ "Object" :
+ TypeUtils.upperFirst(TypeUtils.getClassName(type));
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/UndeclaredThrowableStrategy.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/UndeclaredThrowableStrategy.java
new file mode 100644
index 0000000..2c1b271
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/UndeclaredThrowableStrategy.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+import org.mockito.asm.Attribute;
+import org.mockito.cglib.core.*;
+import org.mockito.cglib.transform.*;
+
+/**
+ * A {@link GeneratorStrategy} suitable for use with {@link org.mockito.cglib.Enhancer} which
+ * causes all undeclared exceptions thrown from within a proxied method to be wrapped
+ * in an alternative exception of your choice.
+ */
+public class UndeclaredThrowableStrategy extends DefaultGeneratorStrategy {
+ private ClassTransformer t;
+
+ /**
+ * Create a new instance of this strategy.
+ * @param wrapper a class which extends either directly or
+ * indirectly from <code>Throwable</code> and which has at least one
+ * constructor that takes a single argument of type
+ * <code>Throwable</code>, for example
+ * <code>java.lang.reflect.UndeclaredThrowableException.class</code>
+ */
+ public UndeclaredThrowableStrategy(Class wrapper) {
+ t = new UndeclaredThrowableTransformer(wrapper);
+ t = new MethodFilterTransformer(TRANSFORM_FILTER, t);
+ }
+
+ private static final MethodFilter TRANSFORM_FILTER = new MethodFilter() {
+ public boolean accept(int access, String name, String desc, String signature, String[] exceptions) {
+ return !TypeUtils.isPrivate(access) && name.indexOf('$') < 0;
+ }
+ };
+
+ protected ClassGenerator transform(ClassGenerator cg) throws Exception {
+ return new TransformingClassGenerator(cg, t);
+ }
+}
+
diff --git a/cglib-and-asm/src/org/mockito/cglib/transform/impl/UndeclaredThrowableTransformer.java b/cglib-and-asm/src/org/mockito/cglib/transform/impl/UndeclaredThrowableTransformer.java
new file mode 100644
index 0000000..09ff741
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/transform/impl/UndeclaredThrowableTransformer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.transform.impl;
+
+import java.lang.reflect.Constructor;
+
+import org.mockito.asm.Attribute;
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+import org.mockito.cglib.transform.*;
+
+public class UndeclaredThrowableTransformer extends ClassEmitterTransformer {
+ private Type wrapper;
+
+ public UndeclaredThrowableTransformer(Class wrapper) {
+ this.wrapper = Type.getType(wrapper);
+ boolean found = false;
+ Constructor[] cstructs = wrapper.getConstructors();
+ for (int i = 0; i < cstructs.length; i++) {
+ Class[] types = cstructs[i].getParameterTypes();
+ if (types.length == 1 && types[0].equals(Throwable.class)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ throw new IllegalArgumentException(wrapper + " does not have a single-arg constructor that takes a Throwable");
+ }
+
+ public CodeEmitter begin_method(int access, final Signature sig, final Type[] exceptions) {
+ CodeEmitter e = super.begin_method(access, sig, exceptions);
+ if (TypeUtils.isAbstract(access) || sig.equals(Constants.SIG_STATIC)) {
+ return e;
+ }
+ return new CodeEmitter(e) {
+ private Block handler;
+ /* init */ {
+ handler = begin_block();
+ }
+ public void visitMaxs(int maxStack, int maxLocals) {
+ handler.end();
+ EmitUtils.wrap_undeclared_throwable(this, handler, exceptions, wrapper);
+ super.visitMaxs(maxStack, maxLocals);
+ }
+ };
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/util/ParallelSorter.java b/cglib-and-asm/src/org/mockito/cglib/util/ParallelSorter.java
new file mode 100644
index 0000000..86ee703
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/util/ParallelSorter.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.util;
+
+import java.lang.reflect.*;
+import java.util.Comparator;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.cglib.core.*;
+
+/**
+ * For the efficient sorting of multiple arrays in parallel.
+ * <p>
+ * Given two arrays of equal length and varying types, the standard
+ * technique for sorting them in parallel is to create a new temporary
+ * object for each row, store the objects in a temporary array, sort the
+ * array using a custom comparator, and the extract the original values
+ * back into their respective arrays. This is wasteful in both time and
+ * memory.
+ * <p>
+ * This class generates bytecode customized to the particular set of
+ * arrays you need to sort, in such a way that both arrays are sorted
+ * in-place, simultaneously.
+ * <p>
+ * Two sorting algorithms are provided.
+ * Quicksort is best when you only need to sort by a single column, as
+ * it requires very few comparisons and swaps. Mergesort is best used
+ * when sorting multiple columns, as it is a "stable" sort--that is, it
+ * does not affect the relative order of equal objects from previous sorts.
+ * <p>
+ * The mergesort algorithm here is an "in-place" variant, which while
+ * slower, does not require a temporary array.
+ *
+ * @author Chris Nokleberg
+ */
+abstract public class ParallelSorter extends SorterTemplate {
+ protected Object[] a;
+ private Comparer comparer;
+
+ protected ParallelSorter() {
+ }
+
+ abstract public ParallelSorter newInstance(Object[] arrays);
+
+ /**
+ * Create a new ParallelSorter object for a set of arrays. You may
+ * sort the arrays multiple times via the same ParallelSorter object.
+ * @param arrays An array of arrays to sort. The arrays may be a mix
+ * of primitive and non-primitive types, but should all be the same
+ * length.
+ * @param loader ClassLoader for generated class, uses "current" if null
+ */
+ public static ParallelSorter create(Object[] arrays) {
+ Generator gen = new Generator();
+ gen.setArrays(arrays);
+ return gen.create();
+ }
+
+ private int len() {
+ return ((Object[])a[0]).length;
+ }
+
+ /**
+ * Sort the arrays using the quicksort algorithm.
+ * @param index array (column) to sort by
+ */
+ public void quickSort(int index) {
+ quickSort(index, 0, len(), null);
+ }
+
+ /**
+ * Sort the arrays using the quicksort algorithm.
+ * @param index array (column) to sort by
+ * @param lo starting array index (row), inclusive
+ * @param hi ending array index (row), exclusive
+ */
+ public void quickSort(int index, int lo, int hi) {
+ quickSort(index, lo, hi, null);
+ }
+
+ /**
+ * Sort the arrays using the quicksort algorithm.
+ * @param index array (column) to sort by
+ * @param cmp Comparator to use if the specified column is non-primitive
+ */
+ public void quickSort(int index, Comparator cmp) {
+ quickSort(index, 0, len(), cmp);
+ }
+
+ /**
+ * Sort the arrays using the quicksort algorithm.
+ * @param index array (column) to sort by
+ * @param lo starting array index (row), inclusive
+ * @param hi ending array index (row), exclusive
+ * @param cmp Comparator to use if the specified column is non-primitive
+ */
+ public void quickSort(int index, int lo, int hi, Comparator cmp) {
+ chooseComparer(index, cmp);
+ super.quickSort(lo, hi - 1);
+ }
+
+ /**
+ * @param index array (column) to sort by
+ */
+ public void mergeSort(int index) {
+ mergeSort(index, 0, len(), null);
+ }
+
+ /**
+ * Sort the arrays using an in-place merge sort.
+ * @param index array (column) to sort by
+ * @param lo starting array index (row), inclusive
+ * @param hi ending array index (row), exclusive
+ */
+ public void mergeSort(int index, int lo, int hi) {
+ mergeSort(index, lo, hi, null);
+ }
+
+ /**
+ * Sort the arrays using an in-place merge sort.
+ * @param index array (column) to sort by
+ * @param lo starting array index (row), inclusive
+ * @param hi ending array index (row), exclusive
+ */
+ public void mergeSort(int index, Comparator cmp) {
+ mergeSort(index, 0, len(), cmp);
+ }
+
+ /**
+ * Sort the arrays using an in-place merge sort.
+ * @param index array (column) to sort by
+ * @param lo starting array index (row), inclusive
+ * @param hi ending array index (row), exclusive
+ * @param cmp Comparator to use if the specified column is non-primitive
+ */
+ public void mergeSort(int index, int lo, int hi, Comparator cmp) {
+ chooseComparer(index, cmp);
+ super.mergeSort(lo, hi - 1);
+ }
+
+ private void chooseComparer(int index, Comparator cmp) {
+ Object array = a[index];
+ Class type = array.getClass().getComponentType();
+ if (type.equals(Integer.TYPE)) {
+ comparer = new IntComparer((int[])array);
+ } else if (type.equals(Long.TYPE)) {
+ comparer = new LongComparer((long[])array);
+ } else if (type.equals(Double.TYPE)) {
+ comparer = new DoubleComparer((double[])array);
+ } else if (type.equals(Float.TYPE)) {
+ comparer = new FloatComparer((float[])array);
+ } else if (type.equals(Short.TYPE)) {
+ comparer = new ShortComparer((short[])array);
+ } else if (type.equals(Byte.TYPE)) {
+ comparer = new ByteComparer((byte[])array);
+ } else if (cmp != null) {
+ comparer = new ComparatorComparer((Object[])array, cmp);
+ } else {
+ comparer = new ObjectComparer((Object[])array);
+ }
+ }
+
+ protected int compare(int i, int j) {
+ return comparer.compare(i, j);
+ }
+
+ interface Comparer {
+ int compare(int i, int j);
+ }
+
+ static class ComparatorComparer implements Comparer {
+ private Object[] a;
+ private Comparator cmp;
+
+ public ComparatorComparer(Object[] a, Comparator cmp) {
+ this.a = a;
+ this.cmp = cmp;
+ }
+
+ public int compare(int i, int j) {
+ return cmp.compare(a[i], a[j]);
+ }
+ }
+
+ static class ObjectComparer implements Comparer {
+ private Object[] a;
+ public ObjectComparer(Object[] a) { this.a = a; }
+ public int compare(int i, int j) {
+ return ((Comparable)a[i]).compareTo(a[j]);
+ }
+ }
+
+ static class IntComparer implements Comparer {
+ private int[] a;
+ public IntComparer(int[] a) { this.a = a; }
+ public int compare(int i, int j) { return a[i] - a[j]; }
+ }
+
+ static class LongComparer implements Comparer {
+ private long[] a;
+ public LongComparer(long[] a) { this.a = a; }
+ public int compare(int i, int j) {
+ long vi = a[i];
+ long vj = a[j];
+ return (vi == vj) ? 0 : (vi > vj) ? 1 : -1;
+ }
+ }
+
+ static class FloatComparer implements Comparer {
+ private float[] a;
+ public FloatComparer(float[] a) { this.a = a; }
+ public int compare(int i, int j) {
+ float vi = a[i];
+ float vj = a[j];
+ return (vi == vj) ? 0 : (vi > vj) ? 1 : -1;
+ }
+ }
+
+ static class DoubleComparer implements Comparer {
+ private double[] a;
+ public DoubleComparer(double[] a) { this.a = a; }
+ public int compare(int i, int j) {
+ double vi = a[i];
+ double vj = a[j];
+ return (vi == vj) ? 0 : (vi > vj) ? 1 : -1;
+ }
+ }
+
+ static class ShortComparer implements Comparer {
+ private short[] a;
+ public ShortComparer(short[] a) { this.a = a; }
+ public int compare(int i, int j) { return a[i] - a[j]; }
+ }
+
+ static class ByteComparer implements Comparer {
+ private byte[] a;
+ public ByteComparer(byte[] a) { this.a = a; }
+ public int compare(int i, int j) { return a[i] - a[j]; }
+ }
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(ParallelSorter.class.getName());
+
+ private Object[] arrays;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return null; // TODO
+ }
+
+ public void setArrays(Object[] arrays) {
+ this.arrays = arrays;
+ }
+
+ public ParallelSorter create() {
+ return (ParallelSorter)super.create(ClassesKey.create(arrays));
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ if (arrays.length == 0) {
+ throw new IllegalArgumentException("No arrays specified to sort");
+ }
+ for (int i = 0; i < arrays.length; i++) {
+ if (!arrays[i].getClass().isArray()) {
+ throw new IllegalArgumentException(arrays[i].getClass() + " is not an array");
+ }
+ }
+ new ParallelSorterEmitter(v, getClassName(), arrays);
+ }
+
+ protected Object firstInstance(Class type) {
+ return ((ParallelSorter)ReflectUtils.newInstance(type)).newInstance(arrays);
+ }
+
+ protected Object nextInstance(Object instance) {
+ return ((ParallelSorter)instance).newInstance(arrays);
+ }
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/util/ParallelSorterEmitter.java b/cglib-and-asm/src/org/mockito/cglib/util/ParallelSorterEmitter.java
new file mode 100644
index 0000000..16bbd40
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/util/ParallelSorterEmitter.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.util;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+class ParallelSorterEmitter extends ClassEmitter {
+ private static final Type PARALLEL_SORTER =
+ TypeUtils.parseType("org.mockito.cglib.util.ParallelSorter");
+ private static final Signature CSTRUCT_OBJECT_ARRAY =
+ TypeUtils.parseConstructor("Object[]");
+ private static final Signature NEW_INSTANCE =
+ new Signature("newInstance", PARALLEL_SORTER, new Type[]{ Constants.TYPE_OBJECT_ARRAY });
+ private static final Signature SWAP =
+ TypeUtils.parseSignature("void swap(int, int)");
+
+ public ParallelSorterEmitter(ClassVisitor v, String className, Object[] arrays) {
+ super(v);
+ begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, PARALLEL_SORTER, null, Constants.SOURCE_FILE);
+ EmitUtils.null_constructor(this);
+ EmitUtils.factory_method(this, NEW_INSTANCE);
+ generateConstructor(arrays);
+ generateSwap(arrays);
+ end_class();
+ }
+
+ private String getFieldName(int index) {
+ return "FIELD_" + index;
+ }
+
+ private void generateConstructor(Object[] arrays) {
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT_ARRAY, null);
+ e.load_this();
+ e.super_invoke_constructor();
+ e.load_this();
+ e.load_arg(0);
+ e.super_putfield("a", Constants.TYPE_OBJECT_ARRAY);
+ for (int i = 0; i < arrays.length; i++) {
+ Type type = Type.getType(arrays[i].getClass());
+ declare_field(Constants.ACC_PRIVATE, getFieldName(i), type, null);
+ e.load_this();
+ e.load_arg(0);
+ e.push(i);
+ e.aaload();
+ e.checkcast(type);
+ e.putfield(getFieldName(i));
+ }
+ e.return_value();
+ e.end_method();
+ }
+
+ private void generateSwap(final Object[] arrays) {
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SWAP, null);
+ for (int i = 0; i < arrays.length; i++) {
+ Type type = Type.getType(arrays[i].getClass());
+ Type component = TypeUtils.getComponentType(type);
+ Local T = e.make_local(type);
+
+ e.load_this();
+ e.getfield(getFieldName(i));
+ e.store_local(T);
+
+ e.load_local(T);
+ e.load_arg(0);
+
+ e.load_local(T);
+ e.load_arg(1);
+ e.array_load(component);
+
+ e.load_local(T);
+ e.load_arg(1);
+
+ e.load_local(T);
+ e.load_arg(0);
+ e.array_load(component);
+
+ e.array_store(component);
+ e.array_store(component);
+ }
+ e.return_value();
+ e.end_method();
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/util/SorterTemplate.java b/cglib-and-asm/src/org/mockito/cglib/util/SorterTemplate.java
new file mode 100644
index 0000000..00bcf3b
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/util/SorterTemplate.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.util;
+
+import java.util.*;
+
+abstract class SorterTemplate {
+ private static final int MERGESORT_THRESHOLD = 12;
+ private static final int QUICKSORT_THRESHOLD = 7;
+
+ abstract protected void swap(int i, int j);
+ abstract protected int compare(int i, int j);
+
+ protected void quickSort(int lo, int hi) {
+ quickSortHelper(lo, hi);
+ insertionSort(lo, hi);
+ }
+
+ private void quickSortHelper(int lo, int hi) {
+ for (;;) {
+ int diff = hi - lo;
+ if (diff <= QUICKSORT_THRESHOLD) {
+ break;
+ }
+ int i = (hi + lo) / 2;
+ if (compare(lo, i) > 0) {
+ swap(lo, i);
+ }
+ if (compare(lo, hi) > 0) {
+ swap(lo, hi);
+ }
+ if (compare(i, hi) > 0) {
+ swap(i, hi);
+ }
+ int j = hi - 1;
+ swap(i, j);
+ i = lo;
+ int v = j;
+ for (;;) {
+ while (compare(++i, v) < 0) {
+ /* nothing */;
+ }
+ while (compare(--j, v) > 0) {
+ /* nothing */;
+ }
+ if (j < i) {
+ break;
+ }
+ swap(i, j);
+ }
+ swap(i, hi - 1);
+ if (j - lo <= hi - i + 1) {
+ quickSortHelper(lo, j);
+ lo = i + 1;
+ } else {
+ quickSortHelper(i + 1, hi);
+ hi = j;
+ }
+ }
+ }
+
+ private void insertionSort(int lo, int hi) {
+ for (int i = lo + 1 ; i <= hi; i++) {
+ for (int j = i; j > lo; j--) {
+ if (compare(j - 1, j) > 0) {
+ swap(j - 1, j);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ protected void mergeSort(int lo, int hi) {
+ int diff = hi - lo;
+ if (diff <= MERGESORT_THRESHOLD) {
+ insertionSort(lo, hi);
+ return;
+ }
+ int mid = lo + diff / 2;
+ mergeSort(lo, mid);
+ mergeSort(mid, hi);
+ merge(lo, mid, hi, mid - lo, hi - mid);
+ }
+
+ private void merge(int lo, int pivot, int hi, int len1, int len2) {
+ if (len1 == 0 || len2 == 0) {
+ return;
+ }
+ if (len1 + len2 == 2) {
+ if (compare(pivot, lo) < 0) {
+ swap(pivot, lo);
+ }
+ return;
+ }
+ int first_cut, second_cut;
+ int len11, len22;
+ if (len1 > len2) {
+ len11 = len1 / 2;
+ first_cut = lo + len11;
+ second_cut = lower(pivot, hi, first_cut);
+ len22 = second_cut - pivot;
+ } else {
+ len22 = len2 / 2;
+ second_cut = pivot + len22;
+ first_cut = upper(lo, pivot, second_cut);
+ len11 = first_cut - lo;
+ }
+ rotate(first_cut, pivot, second_cut);
+ int new_mid = first_cut + len22;
+ merge(lo, first_cut, new_mid, len11, len22);
+ merge(new_mid, second_cut, hi, len1 - len11, len2 - len22);
+ }
+
+ private void rotate(int lo, int mid, int hi) {
+ int lot = lo;
+ int hit = mid - 1;
+ while (lot < hit) {
+ swap(lot++, hit--);
+ }
+ lot = mid; hit = hi - 1;
+ while (lot < hit) {
+ swap(lot++, hit--);
+ }
+ lot = lo; hit = hi - 1;
+ while (lot < hit) {
+ swap(lot++, hit--);
+ }
+ }
+
+ private int lower(int lo, int hi, int val) {
+ int len = hi - lo;
+ while (len > 0) {
+ int half = len / 2;
+ int mid= lo + half;
+ if (compare(mid, val) < 0) {
+ lo = mid + 1;
+ len = len - half -1;
+ } else {
+ len = half;
+ }
+ }
+ return lo;
+ }
+
+ private int upper(int lo, int hi, int val) {
+ int len = hi - lo;
+ while (len > 0) {
+ int half = len / 2;
+ int mid = lo + half;
+ if (compare(val, mid) < 0) {
+ len = half;
+ } else {
+ lo = mid + 1;
+ len = len - half -1;
+ }
+ }
+ return lo;
+ }
+}
diff --git a/cglib-and-asm/src/org/mockito/cglib/util/StringSwitcher.java b/cglib-and-asm/src/org/mockito/cglib/util/StringSwitcher.java
new file mode 100644
index 0000000..a607008
--- /dev/null
+++ b/cglib-and-asm/src/org/mockito/cglib/util/StringSwitcher.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2003 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockito.cglib.util;
+
+import java.util.*;
+
+import org.mockito.asm.ClassVisitor;
+import org.mockito.asm.Label;
+import org.mockito.asm.Type;
+import org.mockito.cglib.core.*;
+
+/**
+ * This class implements a simple String->int mapping for a fixed set of keys.
+ */
+abstract public class StringSwitcher {
+ private static final Type STRING_SWITCHER =
+ TypeUtils.parseType("org.mockito.cglib.util.StringSwitcher");
+ private static final Signature INT_VALUE =
+ TypeUtils.parseSignature("int intValue(String)");
+ private static final StringSwitcherKey KEY_FACTORY =
+ (StringSwitcherKey)KeyFactory.create(StringSwitcherKey.class);
+
+ interface StringSwitcherKey {
+ public Object newInstance(String[] strings, int[] ints, boolean fixedInput);
+ }
+
+ /**
+ * Helper method to create a StringSwitcher.
+ * For finer control over the generated instance, use a new instance of StringSwitcher.Generator
+ * instead of this static method.
+ * @param strings the array of String keys; must be the same length as the value array
+ * @param ints the array of integer results; must be the same length as the key array
+ * @param fixedInput if false, an unknown key will be returned from {@link #intValue} as <code>-1</code>; if true,
+ * the result will be undefined, and the resulting code will be faster
+ */
+ public static StringSwitcher create(String[] strings, int[] ints, boolean fixedInput) {
+ Generator gen = new Generator();
+ gen.setStrings(strings);
+ gen.setInts(ints);
+ gen.setFixedInput(fixedInput);
+ return gen.create();
+ }
+
+ protected StringSwitcher() {
+ }
+
+ /**
+ * Return the integer associated with the given key.
+ * @param s the key
+ * @return the associated integer value, or <code>-1</code> if the key is unknown (unless
+ * <code>fixedInput</code> was specified when this <code>StringSwitcher</code> was created,
+ * in which case the return value for an unknown key is undefined)
+ */
+ abstract public int intValue(String s);
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(StringSwitcher.class.getName());
+
+ private String[] strings;
+ private int[] ints;
+ private boolean fixedInput;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ /**
+ * Set the array of recognized Strings.
+ * @param strings the array of String keys; must be the same length as the value array
+ * @see #setInts
+ */
+ public void setStrings(String[] strings) {
+ this.strings = strings;
+ }
+
+ /**
+ * Set the array of integer results.
+ * @param ints the array of integer results; must be the same length as the key array
+ * @see #setStrings
+ */
+ public void setInts(int[] ints) {
+ this.ints = ints;
+ }
+
+ /**
+ * Configure how unknown String keys will be handled.
+ * @param fixedInput if false, an unknown key will be returned from {@link #intValue} as <code>-1</code>; if true,
+ * the result will be undefined, and the resulting code will be faster
+ */
+ public void setFixedInput(boolean fixedInput) {
+ this.fixedInput = fixedInput;
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return getClass().getClassLoader();
+ }
+
+ /**
+ * Generate the <code>StringSwitcher</code>.
+ */
+ public StringSwitcher create() {
+ setNamePrefix(StringSwitcher.class.getName());
+ Object key = KEY_FACTORY.newInstance(strings, ints, fixedInput);
+ return (StringSwitcher)super.create(key);
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ ClassEmitter ce = new ClassEmitter(v);
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ STRING_SWITCHER,
+ null,
+ Constants.SOURCE_FILE);
+ EmitUtils.null_constructor(ce);
+ final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, INT_VALUE, null);
+ e.load_arg(0);
+ final List stringList = Arrays.asList(strings);
+ int style = fixedInput ? Constants.SWITCH_STYLE_HASHONLY : Constants.SWITCH_STYLE_HASH;
+ EmitUtils.string_switch(e, strings, style, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ e.push(ints[stringList.indexOf(key)]);
+ e.return_value();
+ }
+ public void processDefault() {
+ e.push(-1);
+ e.return_value();
+ }
+ });
+ e.end_method();
+ ce.end_class();
+ }
+
+ protected Object firstInstance(Class type) {
+ return (StringSwitcher)ReflectUtils.newInstance(type);
+ }
+
+ protected Object nextInstance(Object instance) {
+ return instance;
+ }
+ }
+}
diff --git a/src/org/mockito/internal/creation/AbstractMockitoMethodProxy.java b/src/org/mockito/internal/creation/AbstractMockitoMethodProxy.java
new file mode 100644
index 0000000..a6b6bb0
--- /dev/null
+++ b/src/org/mockito/internal/creation/AbstractMockitoMethodProxy.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.creation;
+
+public abstract class AbstractMockitoMethodProxy implements MockitoMethodProxy {
+
+ public Object invokeSuper(Object target, Object[] arguments) throws Throwable {
+ return getMethodProxy().invokeSuper(target, arguments);
+ }
+} \ No newline at end of file
diff --git a/src/org/mockito/internal/creation/AcrossJVMSerializationFeature.java b/src/org/mockito/internal/creation/AcrossJVMSerializationFeature.java
new file mode 100644
index 0000000..94f6b6f
--- /dev/null
+++ b/src/org/mockito/internal/creation/AcrossJVMSerializationFeature.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockito.internal.creation;
+
+import org.mockito.Incubating;
+import org.mockito.exceptions.base.MockitoSerializationIssue;
+import org.mockito.internal.creation.jmock.ClassImposterizer;
+import org.mockito.internal.util.MockUtil;
+import org.mockito.internal.util.reflection.FieldSetter;
+import org.mockito.mock.MockCreationSettings;
+import org.mockito.mock.MockName;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import static org.mockito.internal.util.StringJoiner.join;
+
+/**
+ * This is responsible for serializing a mock, it is enabled if the mock is implementing
+ * {@link Serializable}.
+ *
+ * <p>
+ * The way it works is to enable serialization via the {@link #enableSerializationAcrossJVM(MockCreationSettings)},
+ * if the mock settings is set to be serializable it will add the {@link org.mockito.internal.creation.AcrossJVMSerializationFeature.AcrossJVMMockitoMockSerializable} interface.
+ * This interface defines a the {@link org.mockito.internal.creation.AcrossJVMSerializationFeature.AcrossJVMMockitoMockSerializable#writeReplace()}
+ * whose signature match the one that is looked by the standard Java serialization.
+ * </p>
+ *
+ * <p>
+ * Then in the {@link MethodInterceptorFilter} of mockito, if the <code>writeReplace</code> method is called,
+ * it will use the custom implementation of this class {@link #writeReplace(Object)}. This method has a specific
+ * knowledge on how to serialize a mockito mock that is based on CGLIB.
+ * </p>
+ *
+ * <p><strong>Only one instance per mock! See {@link MethodInterceptorFilter}</strong></p>
+ *
+ * TODO use a proper way to add the interface
+ * TODO offer a way to disable completely this behavior, or maybe enable this behavior only with a specific setting
+ * TODO check the class is mockable in the deserialization side
+ *
+ * @see CglibMockMaker
+ * @see MethodInterceptorFilter
+ * @author Brice Dutheil
+ * @since 1.9.6
+ */
+@Incubating
+public class AcrossJVMSerializationFeature implements Serializable {
+ private static final long serialVersionUID = 7411152578314420778L;
+ private static final String MOCKITO_PROXY_MARKER = "MockitoProxyMarker";
+ private boolean instanceLocalCurrentlySerializingFlag = false;
+ private Lock mutex = new ReentrantLock();
+
+ public boolean isWriteReplace(Method method) {
+ return method.getReturnType() == Object.class
+ && method.getParameterTypes().length == 0
+ && method.getName().equals("writeReplace");
+ }
+
+
+ /**
+ * Custom implementation of the <code>writeReplace</code> method for serialization.
+ *
+ * Here's how it's working and why :
+ * <ol>
+ * <li>
+ * <p>When first entering in this method, it's because some is serializing the mock, with some code like :
+ * <pre class="code"><code class="java">
+ * objectOutputStream.writeObject(mock);
+ * </code></pre>
+ * So, {@link ObjectOutputStream} will track the <code>writeReplace</code> method in the instance and
+ * execute it, which is wanted to replace the mock by another type that will encapsulate the actual mock.
+ * At this point, the code will return an {@link AcrossJVMMockSerializationProxy}.</p>
+ * </li>
+ * <li>
+ * <p>Now, in the constructor {@link AcrossJVMMockSerializationProxy#AcrossJVMMockSerializationProxy(Object)}
+ * the mock is being serialized in a custom way (using {@link MockitoMockObjectOutputStream}) to a
+ * byte array. So basically it means the code is performing double nested serialization of the passed
+ * <code>mockitoMock</code>.</p>
+ *
+ * <p>However the <code>ObjectOutputStream</code> will still detect the custom
+ * <code>writeReplace</code> and execute it.
+ * <em>(For that matter disabling replacement via {@link ObjectOutputStream#enableReplaceObject(boolean)}
+ * doesn't disable the <code>writeReplace</code> call, but just just toggle replacement in the
+ * written stream, <strong><code>writeReplace</code> is always called by
+ * <code>ObjectOutputStream</code></strong>.)</em></p>
+ *
+ * <p>In order to avoid this recursion, obviously leading to a {@link StackOverflowError}, this method is using
+ * a flag that marks the mock as already being replaced, and then shouldn't replace itself again.
+ * <strong>This flag is local to this class</strong>, which means the flag of this class unfortunately needs
+ * to be protected against concurrent access, hence the reentrant lock.</p>
+ * </li>
+ * </ol>
+ *
+ *
+ * @param mockitoMock The Mockito mock to be serialized.
+ * @return A wrapper ({@link AcrossJVMMockSerializationProxy}) to be serialized by the calling ObjectOutputStream.
+ * @throws ObjectStreamException
+ */
+ public Object writeReplace(Object mockitoMock) throws ObjectStreamException {
+ try {
+ // reentrant lock for critical section. could it be improved ?
+ mutex.lock();
+ // mark started flag // per thread, not per instance
+ // temporary loosy hack to avoid stackoverflow
+ if(mockIsCurrentlyBeingReplaced()) {
+ return mockitoMock;
+ }
+ mockReplacementStarted();
+
+ return new AcrossJVMMockSerializationProxy(mockitoMock);
+ } catch (IOException ioe) {
+ MockUtil mockUtil = new MockUtil();
+ MockName mockName = mockUtil.getMockName(mockitoMock);
+ String mockedType = mockUtil.getMockSettings(mockitoMock).getTypeToMock().getCanonicalName();
+ throw new MockitoSerializationIssue(join(
+ "The mock '" + mockName + "' of type '" + mockedType + "'",
+ "The Java Standard Serialization reported an '" + ioe.getClass().getSimpleName() + "' saying :",
+ " " + ioe.getMessage()
+ ), ioe);
+ } finally {
+ // unmark
+ mockReplacementCompleted();
+ mutex.unlock();
+ }
+ }
+
+
+ private void mockReplacementCompleted() {
+ instanceLocalCurrentlySerializingFlag = false;
+ }
+
+
+ private void mockReplacementStarted() {
+ instanceLocalCurrentlySerializingFlag = true;
+ }
+
+
+ private boolean mockIsCurrentlyBeingReplaced() {
+ return instanceLocalCurrentlySerializingFlag;
+ }
+
+
+ /**
+ * Enable serialization serialization that will work across classloaders / and JVM.
+ *
+ * <p>Only enable if settings says the mock should be serializable. In this case add the
+ * {@link AcrossJVMMockitoMockSerializable} to the extra interface list.</p>
+ *
+ * @param settings Mock creation settings.
+ * @param <T> Type param to not be bothered by the generics
+ */
+ public <T> void enableSerializationAcrossJVM(MockCreationSettings<T> settings) {
+ if (settings.isSerializable()) {
+ // havin faith that this set is modifiable
+ // TODO use a proper way to add the interface
+ settings.getExtraInterfaces().add(AcrossJVMMockitoMockSerializable.class);
+ }
+ }
+
+
+ /**
+ * This is the serialization proxy that will encapsulate the real mock data as a byte array.
+ *
+ * <p>When called in the constructor it will serialize the mock in a byte array using a
+ * custom {@link MockitoMockObjectOutputStream} that will annotate the mock class in the stream.
+ * other information are used in this class in order to facilitate deserialization.
+ * </p>
+ *
+ * <p>Deserialization of the mock will be performed by the {@link #readResolve()} method via
+ * the custom {@link MockitoMockObjectInputStream} that will be in charge of creating the mock class.</p>
+ */
+ public static class AcrossJVMMockSerializationProxy implements Serializable {
+
+
+ private static final long serialVersionUID = -7600267929109286514L;
+ private byte[] serializedMock;
+ private Class typeToMock;
+ private Set<Class> extraInterfaces;
+ /**
+ * Creates the wrapper that be used in the serialization stream.
+ *
+ * <p>Immediately serializes the Mockito mock using specifically crafted {@link MockitoMockObjectOutputStream},
+ * in a byte array.</p>
+ *
+ * @param mockitoMock The Mockito mock to serialize.
+ * @throws IOException
+ */
+ public AcrossJVMMockSerializationProxy(Object mockitoMock) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ObjectOutputStream objectOutputStream = new MockitoMockObjectOutputStream(out);
+
+ objectOutputStream.writeObject(mockitoMock);
+
+ objectOutputStream.close();
+ out.close();
+
+ MockCreationSettings mockSettings = new MockUtil().getMockSettings(mockitoMock);
+ this.serializedMock = out.toByteArray();
+ this.typeToMock = mockSettings.getTypeToMock();
+ this.extraInterfaces = mockSettings.getExtraInterfaces();
+ }
+
+ /**
+ * Resolves the proxy to a new deserialized instance of the Mockito mock.
+ *
+ * <p>Uses the custom crafted {@link MockitoMockObjectInputStream} to deserialize the mock.</p>
+ *
+ * @return A deserialized instance of the Mockito mock.
+ * @throws ObjectStreamException
+ */
+ private Object readResolve() throws ObjectStreamException {
+ try {
+ ByteArrayInputStream bis = new ByteArrayInputStream(serializedMock);
+ ObjectInputStream objectInputStream = new MockitoMockObjectInputStream(bis, typeToMock, extraInterfaces);
+
+ Object deserializedMock = objectInputStream.readObject();
+
+ bis.close();
+ objectInputStream.close();
+
+ return deserializedMock;
+ } catch (IOException ioe) {
+ throw new MockitoSerializationIssue(join(
+ "Mockito mock cannot be deserialized to a mock of '" + typeToMock.getCanonicalName() + "'. The error was :",
+ " " + ioe.getMessage(),
+ "If you are unsure what is the reason of this exception, feel free to contact us on the mailing list."
+ ), ioe);
+ } catch (ClassNotFoundException cce) {
+ throw new MockitoSerializationIssue(join(
+ "A class couldn't be found while deserializing a Mockito mock, you should check your classpath. The error was :",
+ " " + cce.getMessage(),
+ "If you are still unsure what is the reason of this exception, feel free to contact us on the mailing list."
+ ), cce);
+ }
+ }
+ }
+
+
+ /**
+ * Special Mockito aware <code>ObjectInputStream</code> that will resolve the Mockito proxy class.
+ *
+ * <p>
+ * This specificaly crafted ObjectInoutStream has the most important role to resolve the Mockito generated
+ * class. It is doing so via the {@link #resolveClass(java.io.ObjectStreamClass)} which looks in the stream
+ * for a Mockito marker. If this marker is found it will try to resolve the mockito class otherwise it
+ * delegates class resolution to the default super behavior.
+ * The mirror method used for serializing the mock is {@link MockitoMockObjectOutputStream#annotateClass(Class)}.
+ * </p>
+ *
+ * <p>
+ * When this marker is found, {@link ClassImposterizer} methods are being used to create the mock class.
+ * <em>Note that behind the <code>ClassImposterizer</code> there is CGLIB and the
+ * {@link org.mockito.internal.creation.jmock.SearchingClassLoader} that will look if this enhanced class has
+ * already been created in an accessible classloader ; so basically this code trusts the ClassImposterizer
+ * code.</em>
+ * </p>
+ */
+ public static class MockitoMockObjectInputStream extends ObjectInputStream {
+ private Class typeToMock;
+ private Set<Class> extraInterfaces;
+
+ public MockitoMockObjectInputStream(InputStream in, Class typeToMock, Set<Class> extraInterfaces) throws IOException {
+ super(in) ;
+ this.typeToMock = typeToMock;
+ this.extraInterfaces = extraInterfaces;
+ enableResolveObject(true); // ensure resolving is enabled
+ }
+
+ /**
+ * Resolve the Mockito proxy class if it is marked as such.
+ *
+ * <p>Uses the fields {@link #typeToMock} and {@link #extraInterfaces} to
+ * create the Mockito proxy class as the <code>ObjectStreamClass</code>
+ * doesn't carry useful information for this purpose.</p>
+ *
+ * @param desc Description of the class in the stream, not used.
+ * @return The class that will be used to deserialize the instance mock.
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ @Override
+ protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+ if (notMarkedAsAMockitoMock(readObject())) {
+ return super.resolveClass(desc);
+ }
+
+ // TODO check the class is mockable in the deserialization side
+ // ClassImposterizer.INSTANCE.canImposterise(typeToMock);
+
+ // create the Mockito mock class before it can even be deserialized
+ ClassImposterizer.INSTANCE.setConstructorsAccessible(typeToMock, true);
+ Class<?> proxyClass = ClassImposterizer.INSTANCE.createProxyClass(
+ typeToMock,
+ extraInterfaces.toArray(new Class[extraInterfaces.size()])
+ );
+
+ hackClassNameToMatchNewlyCreatedClass(desc, proxyClass);
+
+ return proxyClass;
+
+ }
+
+ /**
+ * Hack the <code>name</code> field of the given <code>ObjectStreamClass</code> with
+ * the <code>newProxyClass</code>.
+ *
+ * The parent ObjectInputStream will check the name of the class in the stream matches the name of the one
+ * that is created in this method.
+ *
+ * The CGLIB classes uses a hash of the classloader and/or maybe some other data that allow them to be
+ * relatively unique in a JVM.
+ *
+ * When names differ, which happens when the mock is deserialized in another ClassLoader, a
+ * <code>java.io.InvalidObjectException</code> is thrown, so this part of the code is hacking through
+ * the given <code>ObjectStreamClass</code> to change the name with the newly created class.
+ *
+ * @param descInstance The <code>ObjectStreamClass</code> that will be hacked.
+ * @param proxyClass The proxy class whose name will be applied.
+ * @throws InvalidObjectException
+ */
+ private void hackClassNameToMatchNewlyCreatedClass(ObjectStreamClass descInstance, Class<?> proxyClass) throws ObjectStreamException {
+ try {
+ Field classNameField = descInstance.getClass().getDeclaredField("name");
+ new FieldSetter(descInstance, classNameField).set(proxyClass.getCanonicalName());
+ } catch (NoSuchFieldException nsfe) {
+ // TODO use our own mockito mock serialization exception
+ throw new MockitoSerializationIssue(join(
+ "Wow, the class 'ObjectStreamClass' in the JDK don't have the field 'name',",
+ "this is definitely a bug in our code as it means the JDK team changed a few internal things.",
+ "",
+ "Please report an issue with the JDK used, a code sample and a link to download the JDK would be welcome."
+ ), nsfe);
+ }
+ }
+
+ /**
+ * Read the stream class annotation and identify it as a Mockito mock or not.
+ *
+ * @param marker The marker to identify.
+ * @return <code>true</code> if not marked as a Mockito, <code>false</code> if the class annotation marks a Mockito mock.
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ private boolean notMarkedAsAMockitoMock(Object marker) throws IOException, ClassNotFoundException {
+ return !MOCKITO_PROXY_MARKER.equals(marker);
+ }
+ }
+
+
+ /**
+ * Special Mockito aware <code>ObjectOutputStream</code>.
+ *
+ * <p>
+ * This output stream has the role of marking in the stream the Mockito class. This
+ * marking process is necessary to identify the proxy class that will need to be recreated.
+ *
+ * The mirror method used for deserializing the mock is
+ * {@link MockitoMockObjectInputStream#resolveClass(ObjectStreamClass)}.
+ * </p>
+ *
+ */
+ private static class MockitoMockObjectOutputStream extends ObjectOutputStream {
+ private static final String NOTHING = "";
+ private MockUtil mockUtil = new MockUtil();
+
+ public MockitoMockObjectOutputStream(ByteArrayOutputStream out) throws IOException {
+ super(out);
+ }
+
+ /**
+ * Annotates (marks) the class if this class is a Mockito mock.
+ *
+ * @param cl The class to annotate.
+ * @throws IOException
+ */
+ @Override
+ protected void annotateClass(Class<?> cl) throws IOException {
+ writeObject(mockitoProxyClassMarker(cl));
+ // might be also useful later, for embedding classloader info ...maybe ...maybe not
+ }
+
+ /**
+ * Returns the Mockito marker if this class is a Mockito mock.
+ *
+ * @param cl The class to mark.
+ * @return The marker if this is a Mockito proxy class, otherwise returns a void marker.
+ */
+ private String mockitoProxyClassMarker(Class<?> cl) {
+ if (mockUtil.isMock(cl)) {
+ return MOCKITO_PROXY_MARKER;
+ } else {
+ return NOTHING;
+ }
+ }
+ }
+
+
+ /**
+ * Simple interface that hold a correct <code>writeReplace</code> signature that can be seen by an
+ * <code>ObjectOutputStream</code>.
+ *
+ * It will be applied before the creation of the mock when the mock setting says it should serializable.
+ *
+ * @see #enableSerializationAcrossJVM(org.mockito.mock.MockCreationSettings)
+ */
+ public interface AcrossJVMMockitoMockSerializable {
+ public Object writeReplace() throws java.io.ObjectStreamException;
+ }
+}
diff --git a/src/org/mockito/internal/creation/CglibMockMaker.java b/src/org/mockito/internal/creation/CglibMockMaker.java
new file mode 100644
index 0000000..63893d4
--- /dev/null
+++ b/src/org/mockito/internal/creation/CglibMockMaker.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.creation;
+
+import org.mockito.cglib.proxy.Callback;
+import org.mockito.cglib.proxy.Factory;
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.InternalMockHandler;
+import org.mockito.invocation.MockHandler;
+import org.mockito.mock.MockCreationSettings;
+import org.mockito.plugins.MockMaker;
+import org.mockito.internal.creation.jmock.ClassImposterizer;
+
+/**
+ * A MockMaker that uses cglib to generate mocks on a JVM.
+ */
+public final class CglibMockMaker implements MockMaker {
+
+ public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) {
+ InternalMockHandler mockitoHandler = cast(handler);
+ new AcrossJVMSerializationFeature().enableSerializationAcrossJVM(settings);
+ return ClassImposterizer.INSTANCE.imposterise(
+ new MethodInterceptorFilter(mockitoHandler, settings), settings.getTypeToMock(), settings.getExtraInterfaces());
+ }
+
+ private InternalMockHandler cast(MockHandler handler) {
+ if (!(handler instanceof InternalMockHandler)) {
+ throw new MockitoException("At the moment you cannot provide own implementations of MockHandler." +
+ "\nPlease see the javadocs for the MockMaker interface.");
+ }
+ return (InternalMockHandler) handler;
+ }
+
+ public void resetMock(Object mock, MockHandler newHandler, MockCreationSettings settings) {
+ ((Factory) mock).setCallback(0, new MethodInterceptorFilter(cast(newHandler), settings));
+ }
+
+ public MockHandler getHandler(Object mock) {
+ if (!(mock instanceof Factory)) {
+ return null;
+ }
+ Factory factory = (Factory) mock;
+ Callback callback = factory.getCallback(0);
+ if (!(callback instanceof MethodInterceptorFilter)) {
+ return null;
+ }
+ return ((MethodInterceptorFilter) callback).getHandler();
+ }
+}
diff --git a/src/org/mockito/internal/creation/DelegatingMockitoMethodProxy.java b/src/org/mockito/internal/creation/DelegatingMockitoMethodProxy.java
new file mode 100644
index 0000000..bb2477d
--- /dev/null
+++ b/src/org/mockito/internal/creation/DelegatingMockitoMethodProxy.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.creation;
+
+import org.mockito.cglib.proxy.MethodProxy;
+
+public class DelegatingMockitoMethodProxy extends AbstractMockitoMethodProxy {
+
+ private final MethodProxy methodProxy;
+
+ public DelegatingMockitoMethodProxy(MethodProxy methodProxy) {
+ this.methodProxy = methodProxy;
+ }
+
+ public MethodProxy getMethodProxy() {
+ return methodProxy;
+ }
+} \ No newline at end of file
diff --git a/src/org/mockito/internal/creation/MethodInterceptorFilter.java b/src/org/mockito/internal/creation/MethodInterceptorFilter.java
new file mode 100644
index 0000000..3d7d910
--- /dev/null
+++ b/src/org/mockito/internal/creation/MethodInterceptorFilter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockito.internal.creation;
+
+import org.mockito.cglib.proxy.MethodInterceptor;
+import org.mockito.cglib.proxy.MethodProxy;
+import org.mockito.internal.InternalMockHandler;
+import org.mockito.internal.creation.cglib.CGLIBHacker;
+import org.mockito.internal.invocation.InvocationImpl;
+import org.mockito.internal.invocation.MockitoMethod;
+import org.mockito.internal.invocation.SerializableMethod;
+import org.mockito.internal.invocation.realmethod.FilteredCGLIBProxyRealMethod;
+import org.mockito.internal.progress.SequenceNumber;
+import org.mockito.internal.util.ObjectMethodsGuru;
+import org.mockito.invocation.Invocation;
+import org.mockito.invocation.MockHandler;
+import org.mockito.mock.MockCreationSettings;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+/**
+ * Should be one instance per mock instance, see CglibMockMaker.
+ *
+ *
+ */
+public class MethodInterceptorFilter implements MethodInterceptor, Serializable {
+
+ private static final long serialVersionUID = 6182795666612683784L;
+ private final InternalMockHandler handler;
+ CGLIBHacker cglibHacker = new CGLIBHacker();
+ ObjectMethodsGuru objectMethodsGuru = new ObjectMethodsGuru();
+ private final MockCreationSettings mockSettings;
+ private AcrossJVMSerializationFeature acrossJVMSerializationFeature = new AcrossJVMSerializationFeature();
+
+ public MethodInterceptorFilter(InternalMockHandler handler, MockCreationSettings mockSettings) {
+ this.handler = handler;
+ this.mockSettings = mockSettings;
+ }
+
+ public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
+ throws Throwable {
+ if (objectMethodsGuru.isEqualsMethod(method)) {
+ return proxy == args[0];
+ } else if (objectMethodsGuru.isHashCodeMethod(method)) {
+ return hashCodeForMock(proxy);
+ } else if (acrossJVMSerializationFeature.isWriteReplace(method)) {
+ return acrossJVMSerializationFeature.writeReplace(proxy);
+ }
+
+ MockitoMethodProxy mockitoMethodProxy = createMockitoMethodProxy(methodProxy);
+ cglibHacker.setMockitoNamingPolicy(mockitoMethodProxy);
+
+ MockitoMethod mockitoMethod = createMockitoMethod(method);
+
+ FilteredCGLIBProxyRealMethod realMethod = new FilteredCGLIBProxyRealMethod(mockitoMethodProxy);
+ Invocation invocation = new InvocationImpl(proxy, mockitoMethod, args, SequenceNumber.next(), realMethod);
+ return handler.handle(invocation);
+ }
+
+ public MockHandler getHandler() {
+ return handler;
+ }
+
+ private int hashCodeForMock(Object mock) {
+ return System.identityHashCode(mock);
+ }
+
+ public MockitoMethodProxy createMockitoMethodProxy(MethodProxy methodProxy) {
+ if (mockSettings.isSerializable())
+ return new SerializableMockitoMethodProxy(methodProxy);
+ return new DelegatingMockitoMethodProxy(methodProxy);
+ }
+
+ public MockitoMethod createMockitoMethod(Method method) {
+ if (mockSettings.isSerializable()) {
+ return new SerializableMethod(method);
+ } else {
+ return new DelegatingMethod(method);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/org/mockito/internal/creation/MockitoMethodProxy.java b/src/org/mockito/internal/creation/MockitoMethodProxy.java
new file mode 100644
index 0000000..384ca61
--- /dev/null
+++ b/src/org/mockito/internal/creation/MockitoMethodProxy.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.creation;
+
+import org.mockito.cglib.proxy.MethodProxy;
+
+public interface MockitoMethodProxy {
+
+ Object invokeSuper(Object target, Object[] arguments) throws Throwable;
+
+ MethodProxy getMethodProxy();
+
+} \ No newline at end of file
diff --git a/src/org/mockito/internal/creation/SerializableMockitoMethodProxy.java b/src/org/mockito/internal/creation/SerializableMockitoMethodProxy.java
new file mode 100644
index 0000000..0646083
--- /dev/null
+++ b/src/org/mockito/internal/creation/SerializableMockitoMethodProxy.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.creation;
+
+import java.io.Serializable;
+
+import org.mockito.cglib.proxy.MethodProxy;
+import org.mockito.internal.util.reflection.Whitebox;
+
+public class SerializableMockitoMethodProxy extends AbstractMockitoMethodProxy implements Serializable {
+
+ private static final long serialVersionUID = -5337859962876770632L;
+ private final Class<?> c1;
+ private final Class<?> c2;
+ private final String desc;
+ private final String name;
+ private final String superName;
+ private transient MethodProxy methodProxy;
+
+ public SerializableMockitoMethodProxy(MethodProxy methodProxy) {
+ Object info = Whitebox.getInternalState(methodProxy, "createInfo");
+ c1 = (Class<?>) Whitebox.getInternalState(info, "c1");
+ c2 = (Class<?>) Whitebox.getInternalState(info, "c2");
+ desc = methodProxy.getSignature().getDescriptor();
+ name = methodProxy.getSignature().getName();
+ superName = methodProxy.getSuperName();
+ this.methodProxy = methodProxy;
+ }
+
+ public MethodProxy getMethodProxy() {
+ if (methodProxy == null)
+ methodProxy = MethodProxy.create(c1, c2, desc, name, superName);
+ return methodProxy;
+ }
+} \ No newline at end of file
diff --git a/src/org/mockito/internal/creation/cglib/CGLIBHacker.java b/src/org/mockito/internal/creation/cglib/CGLIBHacker.java
new file mode 100644
index 0000000..e0ce914
--- /dev/null
+++ b/src/org/mockito/internal/creation/cglib/CGLIBHacker.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.creation.cglib;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+
+import org.mockito.internal.creation.MockitoMethodProxy;
+import org.mockito.cglib.proxy.MethodProxy;
+
+public class CGLIBHacker implements Serializable {
+
+ private static final long serialVersionUID = -4389233991416356668L;
+
+ public void setMockitoNamingPolicy(MockitoMethodProxy mockitoMethodProxy) {
+ try {
+ MethodProxy methodProxy = mockitoMethodProxy.getMethodProxy();
+ Field createInfoField = reflectOnCreateInfo(methodProxy);
+ createInfoField.setAccessible(true);
+ Object createInfo = createInfoField.get(methodProxy);
+ Field namingPolicyField = createInfo.getClass().getDeclaredField("namingPolicy");
+ namingPolicyField.setAccessible(true);
+ if (namingPolicyField.get(createInfo) == null) {
+ namingPolicyField.set(createInfo, MockitoNamingPolicy.INSTANCE);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Unable to set MockitoNamingPolicy on cglib generator which creates FastClasses", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Field reflectOnCreateInfo(MethodProxy methodProxy) throws SecurityException, NoSuchFieldException {
+
+ Class cglibMethodProxyClass = methodProxy.getClass();
+ // in case methodProxy was extended by user, let's traverse the object
+ // graph to find the cglib methodProxy
+ // with all the fields we would like to change
+ while (cglibMethodProxyClass != MethodProxy.class) {
+ cglibMethodProxyClass = methodProxy.getClass().getSuperclass();
+ }
+ return cglibMethodProxyClass.getDeclaredField("createInfo");
+ }
+} \ No newline at end of file
diff --git a/src/org/mockito/internal/creation/cglib/MockitoNamingPolicy.java b/src/org/mockito/internal/creation/cglib/MockitoNamingPolicy.java
new file mode 100644
index 0000000..c646ccd
--- /dev/null
+++ b/src/org/mockito/internal/creation/cglib/MockitoNamingPolicy.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.creation.cglib;
+
+import org.mockito.cglib.core.DefaultNamingPolicy;
+
+public class MockitoNamingPolicy extends DefaultNamingPolicy {
+
+ public static final MockitoNamingPolicy INSTANCE = new MockitoNamingPolicy();
+
+ @Override
+ protected String getTag() {
+ return "ByMockitoWithCGLIB";
+ }
+} \ No newline at end of file
diff --git a/src/org/mockito/internal/creation/cglib/package.html b/src/org/mockito/internal/creation/cglib/package.html
new file mode 100644
index 0000000..d195d54
--- /dev/null
+++ b/src/org/mockito/internal/creation/cglib/package.html
@@ -0,0 +1,6 @@
+<!--
+ ~ Copyright (c) 2007 Mockito contributors
+ ~ This program is made available under the terms of the MIT License.
+ -->
+
+<body>CGLIB related stuff</body> \ No newline at end of file
diff --git a/src/org/mockito/internal/creation/jmock/ClassImposterizer.java b/src/org/mockito/internal/creation/jmock/ClassImposterizer.java
new file mode 100644
index 0000000..2e5ebcb
--- /dev/null
+++ b/src/org/mockito/internal/creation/jmock/ClassImposterizer.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.creation.jmock;
+
+import org.mockito.cglib.core.CodeGenerationException;
+import org.mockito.cglib.core.NamingPolicy;
+import org.mockito.cglib.core.Predicate;
+import org.mockito.cglib.proxy.*;
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.configuration.GlobalConfiguration;
+import org.mockito.internal.creation.cglib.MockitoNamingPolicy;
+import org.objenesis.ObjenesisStd;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.List;
+
+import static org.mockito.internal.util.StringJoiner.join;
+
+/**
+ * Thanks to jMock guys for this handy class that wraps all the cglib magic.
+ */
+public class ClassImposterizer {
+
+ public static final ClassImposterizer INSTANCE = new ClassImposterizer();
+
+ private ClassImposterizer() {}
+
+ //TODO: in order to provide decent exception message when objenesis is not found,
+ //have a constructor in this class that tries to instantiate ObjenesisStd and if it fails then show decent exception that dependency is missing
+ //TODO: for the same reason catch and give better feedback when hamcrest core is not found.
+ private ObjenesisStd objenesis = new ObjenesisStd(new GlobalConfiguration().enableClassCache());
+
+ private static final NamingPolicy NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES = new MockitoNamingPolicy() {
+ @Override
+ public String getClassName(String prefix, String source, Object key, Predicate names) {
+ return "codegen." + super.getClassName(prefix, source, key, names);
+ }
+ };
+
+ private static final CallbackFilter IGNORE_BRIDGE_METHODS = new CallbackFilter() {
+ public int accept(Method method, List<Method> allMethods) {
+ return method.isBridge() ? 1 : 0;
+ }
+ };
+
+ public <T> T imposterise(final MethodInterceptor interceptor, Class<T> mockedType, Collection<Class> ancillaryTypes) {
+ return imposterise(interceptor, mockedType, ancillaryTypes.toArray(new Class[ancillaryTypes.size()]));
+ }
+
+ public <T> T imposterise(final MethodInterceptor interceptor, Class<T> mockedType, Class<?>... ancillaryTypes) {
+ Class<?> proxyClass = null;
+ Object proxyInstance = null;
+ try {
+ setConstructorsAccessible(mockedType, true);
+ proxyClass = createProxyClass(mockedType, ancillaryTypes);
+ proxyInstance = createProxy(proxyClass, interceptor);
+ return mockedType.cast(proxyInstance);
+ } catch (ClassCastException cce) {
+ // NPE unlikely to happen because CCE will only happen on the cast statement
+ throw new MockitoException(join(
+ "ClassCastException occurred while creating the mockito proxy :",
+ " class to imposterize : '" + mockedType.getCanonicalName() + "', loaded by classloader : '" + mockedType.getClassLoader() + "'",
+ " imposterizing class : '" + proxyClass.getCanonicalName() + "', loaded by classloader : '" + proxyClass.getClassLoader() + "'",
+ " proxy instance class : '" + proxyInstance.getClass().getCanonicalName() + "', loaded by classloader : '" + proxyInstance.getClass().getClassLoader() + "'",
+ "",
+ "You might experience classloading issues, disabling the Objenesis cache *might* help (see MockitoConfiguration)"
+ ), cce);
+ } finally {
+ setConstructorsAccessible(mockedType, false);
+ }
+ }
+
+ public void setConstructorsAccessible(Class<?> mockedType, boolean accessible) {
+ for (Constructor<?> constructor : mockedType.getDeclaredConstructors()) {
+ constructor.setAccessible(accessible);
+ }
+ }
+
+ public Class<?> createProxyClass(Class<?> mockedType, Class<?>... interfaces) {
+ if (mockedType == Object.class) {
+ mockedType = ClassWithSuperclassToWorkAroundCglibBug.class;
+ }
+
+ Enhancer enhancer = new Enhancer() {
+ @Override
+ @SuppressWarnings("unchecked")
+ protected void filterConstructors(Class sc, List constructors) {
+ // Don't filter
+ }
+ };
+ enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(mockedType));
+ enhancer.setUseFactory(true);
+ if (mockedType.isInterface()) {
+ enhancer.setSuperclass(Object.class);
+ enhancer.setInterfaces(prepend(mockedType, interfaces));
+ } else {
+ enhancer.setSuperclass(mockedType);
+ enhancer.setInterfaces(interfaces);
+ }
+ enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class});
+ enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS);
+ if (mockedType.getSigners() != null) {
+ enhancer.setNamingPolicy(NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES);
+ } else {
+ enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE);
+ }
+
+ enhancer.setSerialVersionUID(42L);
+
+ try {
+ return enhancer.createClass();
+ } catch (CodeGenerationException e) {
+ if (Modifier.isPrivate(mockedType.getModifiers())) {
+ throw new MockitoException("\n"
+ + "Mockito cannot mock this class: " + mockedType
+ + ".\n"
+ + "Most likely it is a private class that is not visible by Mockito");
+ }
+ throw new MockitoException("\n"
+ + "Mockito cannot mock this class: " + mockedType
+ + "\n"
+ + "Mockito can only mock visible & non-final classes."
+ + "\n"
+ + "If you're not sure why you're getting this error, please report to the mailing list.", e);
+ }
+ }
+
+ private Object createProxy(Class<?> proxyClass, final MethodInterceptor interceptor) {
+ Factory proxy = (Factory) objenesis.newInstance(proxyClass);
+ proxy.setCallbacks(new Callback[] {interceptor, SerializableNoOp.SERIALIZABLE_INSTANCE });
+ return proxy;
+ }
+
+ private Class<?>[] prepend(Class<?> first, Class<?>... rest) {
+ Class<?>[] all = new Class<?>[rest.length+1];
+ all[0] = first;
+ System.arraycopy(rest, 0, all, 1, rest.length);
+ return all;
+ }
+
+ public static class ClassWithSuperclassToWorkAroundCglibBug {}
+
+} \ No newline at end of file
diff --git a/src/org/mockito/internal/creation/jmock/SearchingClassLoader.java b/src/org/mockito/internal/creation/jmock/SearchingClassLoader.java
new file mode 100644
index 0000000..85d741e
--- /dev/null
+++ b/src/org/mockito/internal/creation/jmock/SearchingClassLoader.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.creation.jmock;
+
+import static java.lang.Thread.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Thanks to jMock guys for this ClassLoader.
+ */
+public class SearchingClassLoader extends ClassLoader {
+ private final ClassLoader nextToSearch;
+
+ public SearchingClassLoader(ClassLoader parent, ClassLoader nextToSearch) {
+ super(parent);
+ this.nextToSearch = nextToSearch;
+ }
+
+ public static ClassLoader combineLoadersOf(Class<?>... classes) {
+ return combineLoadersOf(classes[0], classes);
+ }
+
+ private static ClassLoader combineLoadersOf(Class<?> first, Class<?>... others) {
+ List<ClassLoader> loaders = new ArrayList<ClassLoader>();
+
+ addIfNewElement(loaders, first.getClassLoader());
+ for (Class<?> c : others) {
+ addIfNewElement(loaders, c.getClassLoader());
+ }
+
+ // To support Eclipse Plug-in tests.
+ // In an Eclipse plug-in, jMock itself will not be on the system class loader
+ // but in the class loader of the plug-in.
+ //
+ // Note: I've been unable to reproduce the error in jMock's test suite.
+ addIfNewElement(loaders, SearchingClassLoader.class.getClassLoader());
+
+ // To support the Maven Surefire plugin.
+ // Note: I've been unable to reproduce the error in jMock's test suite.
+ addIfNewElement(loaders, currentThread().getContextClassLoader());
+
+ //Had to comment that out because it didn't work with in-container Spring tests
+ //addIfNewElement(loaders, ClassLoader.getSystemClassLoader());
+
+ return combine(loaders);
+ }
+
+ private static ClassLoader combine(List<ClassLoader> parentLoaders) {
+ ClassLoader loader = parentLoaders.get(parentLoaders.size()-1);
+
+ for (int i = parentLoaders.size()-2; i >= 0; i--) {
+ loader = new SearchingClassLoader(parentLoaders.get(i), loader);
+ }
+
+ return loader;
+ }
+
+ private static void addIfNewElement(List<ClassLoader> loaders, ClassLoader c) {
+ if (c != null && !loaders.contains(c)) {
+ loaders.add(c);
+ }
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (nextToSearch != null) {
+ return nextToSearch.loadClass(name);
+ } else {
+ return super.findClass(name); // will throw ClassNotFoundException
+ }
+ }
+} \ No newline at end of file
diff --git a/src/org/mockito/internal/creation/jmock/SerializableNoOp.java b/src/org/mockito/internal/creation/jmock/SerializableNoOp.java
new file mode 100644
index 0000000..c5608e7
--- /dev/null
+++ b/src/org/mockito/internal/creation/jmock/SerializableNoOp.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.creation.jmock;
+
+import org.mockito.cglib.proxy.Callback;
+import org.mockito.cglib.proxy.NoOp;
+
+import java.io.Serializable;
+
+/**
+ * Offer a Serializable implementation of the NoOp CGLIB callback.
+ */
+public class SerializableNoOp implements NoOp, Serializable {
+
+ private static final long serialVersionUID = 7434976328690189159L;
+ public static final Callback SERIALIZABLE_INSTANCE = new SerializableNoOp();
+
+}
diff --git a/src/org/mockito/internal/creation/jmock/jmock-license.txt b/src/org/mockito/internal/creation/jmock/jmock-license.txt
new file mode 100644
index 0000000..4c56c9c
--- /dev/null
+++ b/src/org/mockito/internal/creation/jmock/jmock-license.txt
@@ -0,0 +1,25 @@
+Copyright (c) 2000-2007, jMock.org
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of
+conditions and the following disclaimer. Redistributions in binary form must reproduce
+the above copyright notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the distribution.
+
+Neither the name of jMock nor the names of its contributors may be used to endorse
+or promote products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE. \ No newline at end of file
diff --git a/src/org/mockito/internal/creation/jmock/package.html b/src/org/mockito/internal/creation/jmock/package.html
new file mode 100644
index 0000000..39c142b
--- /dev/null
+++ b/src/org/mockito/internal/creation/jmock/package.html
@@ -0,0 +1,6 @@
+<!--
+ ~ Copyright (c) 2007 Mockito contributors
+ ~ This program is made available under the terms of the MIT License.
+ -->
+
+<body>Borrowed from jmock codebase</body> \ No newline at end of file
diff --git a/src/org/mockito/internal/invocation/realmethod/CGLIBProxyRealMethod.java b/src/org/mockito/internal/invocation/realmethod/CGLIBProxyRealMethod.java
new file mode 100644
index 0000000..140ae8e
--- /dev/null
+++ b/src/org/mockito/internal/invocation/realmethod/CGLIBProxyRealMethod.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.invocation.realmethod;
+
+import java.io.Serializable;
+
+import org.mockito.internal.creation.MockitoMethodProxy;
+
+
+public class CGLIBProxyRealMethod implements RealMethod, HasCGLIBMethodProxy, Serializable {
+
+ private static final long serialVersionUID = -4596470901191501582L;
+ private final MockitoMethodProxy methodProxy;
+
+ public CGLIBProxyRealMethod(MockitoMethodProxy methodProxy) {
+ this.methodProxy = methodProxy;
+ }
+
+ public Object invoke(Object target, Object[] arguments) throws Throwable {
+ return methodProxy.invokeSuper(target, arguments);
+ }
+
+ public MockitoMethodProxy getMethodProxy() {
+ return methodProxy;
+ }
+}
diff --git a/src/org/mockito/internal/invocation/realmethod/FilteredCGLIBProxyRealMethod.java b/src/org/mockito/internal/invocation/realmethod/FilteredCGLIBProxyRealMethod.java
new file mode 100644
index 0000000..39c3b86
--- /dev/null
+++ b/src/org/mockito/internal/invocation/realmethod/FilteredCGLIBProxyRealMethod.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.invocation.realmethod;
+
+import org.mockito.internal.creation.MockitoMethodProxy;
+import org.mockito.internal.exceptions.stacktrace.ConditionalStackTraceFilter;
+
+import java.io.Serializable;
+
+public class FilteredCGLIBProxyRealMethod implements RealMethod, HasCGLIBMethodProxy, Serializable {
+
+ private static final long serialVersionUID = 3596550785818938496L;
+ private final RealMethod realMethod;
+
+ public FilteredCGLIBProxyRealMethod(MockitoMethodProxy methodProxy) {
+ this(new CGLIBProxyRealMethod(methodProxy));
+ }
+
+ public FilteredCGLIBProxyRealMethod(RealMethod realMethod) {
+ this.realMethod = realMethod;
+ }
+
+ public Object invoke(Object target, Object[] arguments) throws Throwable {
+ try {
+ return realMethod.invoke(target, arguments);
+ } catch (Throwable t) {
+ new ConditionalStackTraceFilter().filter(t);
+ throw t;
+ }
+ }
+
+ public MockitoMethodProxy getMethodProxy() {
+ return ((HasCGLIBMethodProxy) realMethod).getMethodProxy();
+ }
+} \ No newline at end of file
diff --git a/src/org/mockito/internal/invocation/realmethod/HasCGLIBMethodProxy.java b/src/org/mockito/internal/invocation/realmethod/HasCGLIBMethodProxy.java
new file mode 100644
index 0000000..299bc16
--- /dev/null
+++ b/src/org/mockito/internal/invocation/realmethod/HasCGLIBMethodProxy.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.invocation.realmethod;
+
+import java.io.Serializable;
+
+import org.mockito.internal.creation.MockitoMethodProxy;
+
+public interface HasCGLIBMethodProxy extends Serializable {
+
+ MockitoMethodProxy getMethodProxy();
+}
diff --git a/update_source.sh b/update_source.sh
index f64bf87..2f0d802 100755
--- a/update_source.sh
+++ b/update_source.sh
@@ -2,28 +2,22 @@
#
# Copyright 2013 The Android Open Source Project.
#
-# Retrieves the current Mockito source code into the current direcory, exlcuding portions related
-# to constructing Mock objects in the JVM.
+# Retrieves the current Mockito source code into the current directory, excluding portions related
+# to mockito's internal build system and javadoc.
SOURCE="git://github.com/mockito/mockito.git"
INCLUDE="
LICENSE
+ cglib-and-asm
src
"
EXCLUDE="
- src/org/mockito/internal/creation/cglib
- src/org/mockito/internal/creation/jmock
- src/org/mockito/internal/creation/AbstractMockitoMethodProxy.java
- src/org/mockito/internal/creation/AcrossJVMSerializationFeature.java
- src/org/mockito/internal/creation/CglibMockMaker.java
- src/org/mockito/internal/creation/DelegatingMockitoMethodProxy.java
- src/org/mockito/internal/creation/MethodInterceptorFilter.java
- src/org/mockito/internal/creation/MockitoMethodProxy.java
- src/org/mockito/internal/creation/SerializableMockitoMethodProxy.java
- src/org/mockito/internal/invocation/realmethod/FilteredCGLIBProxyRealMethod.java
- src/org/mockito/internal/invocation/realmethod/CGLIBProxyRealMethod.java
- src/org/mockito/internal/invocation/realmethod/HasCGLIBMethodProxy.java
+ cglib-and-asm/lib
+ cglib-and-asm/.project
+ cglib-and-asm/.classpath
+ cglib-and-asm/build.gradle
+ cglib-and-asm/mockito-repackaged.iml
"
working_dir="$(mktemp -d)"